;;; chess-var.el --- Manipulate variations  -*- lexical-binding: t; -*-

;; Copyright (C) 2014-2022 Free Software Foundation, Inc.

;; This is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 3, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; A chess variations is a simple list of plies.  This module provides
;; an abstraction layer for applications.

;;; Code:

(require 'chess-algebraic)
(require 'chess-ply)
(eval-when-compile (require 'cl-lib))

(defsubst chess-var-plies (var)
  "Return the plies of VAR."
  (cl-assert var)
  var)

(defsubst chess-var-pos (var &optional index)
  "Return the position related to VAR's INDEX ply."
  (cl-assert var)
  (chess-ply-pos (chess-var-ply var index)))

(defsubst chess-var-index (var)
  "Return the VAR's current position index."
  (cl-assert var)
  (1- (length (chess-var-plies var))))

(defsubst chess-var-seq (var)
  "Return the current VAR sequence."
  (cl-assert var)
  (1+ (/ (chess-var-index var) 2)))

(defsubst chess-var-side-to-move (var &optional index)
  "Return the color whose move it is in VAR at INDEX (or at the last position
of the variation if INDEX is nil)."
  (cl-assert var)
  (chess-pos-side-to-move (chess-var-pos var index)))

(defun chess-var-ply (var &optional index)
  "Return VAR's INDEXth ply."
  (cl-assert var)
  (if index
      (nth index (chess-var-plies var))
    (car (last (chess-var-plies var)))))

(defun chess-var-add-ply (var ply)
  "Add to VAR the given PLY."
  (cl-assert var)
  (cl-assert (listp ply))
  (let ((plies (chess-var-plies var)))
    (cl-assert plies)
    (nconc plies (list ply))))

(defsubst chess-var-create (&optional position)
  "Create a new chess variation object.
Optionally use the given starting POSITION."
  (list (chess-ply-create* (or position chess-starting-position))))

(defun chess-var-move (var ply)
  "Make a move in the current VAR by applying the changes of PLY.
This creates a new position and adds it to the main variation.
The `changes' of the last ply reflect whether the var is currently in
progress (nil), if it is drawn, resigned, mate, etc."
  (cl-assert var)
  (cl-assert (listp ply))
  (let ((current-ply (chess-var-ply var))
	(changes (chess-ply-changes ply)))
    (if (chess-ply-final-p current-ply)
	(chess-error 'add-to-completed))
    (cl-assert (eq (chess-ply-pos ply) (chess-ply-pos current-ply)))
    (chess-ply-set-changes current-ply changes)
    (chess-var-add-ply var (chess-ply-create*
			    (chess-ply-next-pos current-ply)))))

(defun chess-var-to-algebraic (var &optional type)
  "Reveal the plies of VAR by converting them to algebraic notation.
Optional argument TYPE defines the type of algebraic notation to use
(`:san', `:lan' or `:fan'."
  (mapconcat (lambda (ply)
	       (chess-ply-to-algebraic ply type))
	     (if (chess-ply-final-p (chess-var-ply var))
		 (chess-var-plies var)
	       (butlast (chess-var-plies var)))
	     " "))

(provide 'chess-var)

;;; chess-var.el ends here
