#-*- coding:utf-8 -*-

#  Pybik -- A 3 dimensional magic cube game.
#  Copyright © 2009-2013  B. Clausius <barcc@gmx.de>
#
#  This program 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 of the License, or
#  (at your option) any later version.
#
#  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

# Ported from GNUbik
# Original filename: ui.c
# Original copyright and license: GPL3+
#                  1998,  2003  John Darrington
#                  2004  John Darrington,  Dale Mellor



from .debug import *    # pylint: disable=W0614,W0401

from .model import empty_model
from . import cubestate
from .moves import MoveQueue


class GameState (object):
    def __init__(self):
        self.mark_before = False
        self.initial_cube_state = cubestate.CubeState(empty_model)
        self.initial_cube_state.set_solved()
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves = MoveQueue()
        
    def copy(self):
        gamestate = GameState()
        gamestate.mark_before = self.mark_before
        gamestate.initial_cube_state = self.initial_cube_state.copy()
        gamestate.current_cube_state = self.current_cube_state.copy()
        gamestate.all_moves = self.all_moves.copy()
        return gamestate
        
    def set_state(self, model, blocks, moves, position):
        self.initial_cube_state = cubestate.CubeState(model)
        try:
            self.initial_cube_state.parse_block(blocks)
        except ValueError as e:
            debug('Invalid block list:', *e.args)
            debug('  start with solved game')
            self.initial_cube_state.set_solved()
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves = MoveQueue()
        if moves.startswith('native:'):
            moves = moves.split(':', 1)[1].lstrip()
        else:
            moves = ''
        pos, unused_cpos = self.all_moves.parse(moves, position, model)
        self.do_fast_forward(to_pos=pos)
        
    def get_state(self):
        model = self.initial_cube_state.model
        blocks = self.initial_cube_state.format_block()
        moves, position, unused_markup = self.all_moves.format(model)
        return model, blocks, 'native: ' + moves, position
        
    def set_model(self, model):
        self.initial_cube_state.set_model(model)
        self.current_cube_state.set_model(model)
        
    def new_game(self, model, solved):
        self.initial_cube_state = cubestate.CubeState(model)
        self.initial_cube_state.set_solved()
        if not solved:
            self.initial_cube_state.randomize()
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves.reset()
        
    ### Funktions to control the cube for use in callbacks and plugins
    
    def request_back(self):
        '''One step back in the sequence of moves'''
        if self.all_moves.at_start():
            return None
        self.mark_before = self.all_moves.is_mark_current()
        self.all_moves.retard()
        move = self.all_moves.current().inverted()
        self.current_cube_state.rotate_slice(move)
        return move
        
    def request_next(self):
        '''One step forward in the sequence of moves'''
        move = self.all_moves.current()
        if move:
            self.mark_before = self.all_moves.is_mark_current()
            self.current_cube_state.rotate_slice(move)
            self.all_moves.advance()
        return move
        
    def request_rotation(self, maxis, mslice, mdir):
        '''Make one new move.
        
        The usual rotation request,  called when user uses mouse to rotate the cube by
        hand. The move is put into the `current' place,  and all the moves in
        all_moves afterwards are zapped.'''
        self.all_moves.push_current((maxis, mslice, mdir))
        
    def do_rewind_to_mark(self):
        if self.all_moves.at_start():
            return False
        while True:
            self.all_moves.retard()
            move = self.all_moves.current().inverted()
            self.current_cube_state.rotate_slice(move)
            if self.all_moves.is_mark_current():
                break
        return True
        
    def do_rewind_to_start(self):
        # for edit mode cloning is a must, even if already at start
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves.rewind_start()
            
    def do_fast_forward(self, to_pos=-1):
        '''Go to end of the sequence of moves.
        
        All the moves take place on the cube,  not via the animation
        routines,  so the effect is to show the result instantaneously.'''
        move = self.all_moves.current()
        while move:
            if to_pos == self.all_moves.current_place:
                break
            self.current_cube_state.rotate_slice(move)
            self.all_moves.advance()
            if to_pos < 0 and self.all_moves.is_mark_current():
                break
            move = self.all_moves.current()
        
    def set_from_formula(self, code, pos):
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves.rewind_start()
        self.all_moves.reset()
        pos, unused_cpos = self.all_moves.parse(code, pos, self.current_cube_state.model)
        self.do_fast_forward(to_pos=pos)
        
    def set_as_initial_state(self):
        self.initial_cube_state = self.current_cube_state.copy()
        self.all_moves.truncate_before()
        
    def invert_moves(self):
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves.invert()
        pos = self.all_moves.current_place
        self.all_moves.rewind_start()
        self.do_fast_forward(to_pos=pos)
        
    def normalize_complete_rotations(self):
        self.current_cube_state = self.initial_cube_state.copy()
        self.all_moves.normalize_complete_rotations(self.initial_cube_state.model)
        pos = self.all_moves.current_place
        self.all_moves.rewind_start()
        self.do_fast_forward(to_pos=pos)
        
    def swap_block(self, blockpos, maxis, mslice, mdir):
        self.initial_cube_state.swap_block(blockpos, maxis, mslice, mdir)
        # Because initial_cube_state changed, current_cube_state must be cloned now,
        # do_rewind_to_start does that.
        self.do_rewind_to_start()
        
    def rotate_block(self, blockpos, rdir):
        self.initial_cube_state.rotate_block(blockpos, rdir)
        self.do_rewind_to_start()
        

