#!/usr/bin/python3
# -*- coding: utf-8 -*-

#  Copyright © 2012-2015  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/>.

import sys, os
sys.path.insert(0, '.')
import cmd
from contextlib import suppress

from pybiklib import config, cubestate, model
from pybiklib.moves import MoveQueue
from tools import patterncube


def convert(salg):
    moves = {
        "": "",
        "F":   "f",    "B":   "b",    "R":   "r",    "L":  "l",    "U":   "u",    "D":   "d",
        "F'":  "f-",   "B'":  "b-",   "R'":  "r-",   "L'": "l-",   "U'":  "u-",   "D'":  "d-",
        "F2":  "ff",   "B2":  "bb",   "R2":  "rr",   "L2": "ll",   "U2":  "uu",   "D2":  "dd",
        "F'2": "ff",   "B'2": "bb",   "R'2": "rr",   "L'2": "ll",  "U'2": "uu",   "D'2": "dd",
        "M":   "r2-",  "E":   "u2-",  "S":   "f2",
        "M'":  "r2",   "E'":  "u2",   "S'":  "f2-",
        "M2":  "r2r2", "E2":  "u2u2", "S2":  "f2f2",
        "M2'": "r2r2", "E2'": "u2u2", "S2'": "f2f2",
        "x":   "R",   "y":   "U",   "z":   "F",
        "x'":  "R-",  "y'":  "U-",  "z'":  "F-",
        "x2":  "RR",  "y2":  "UU",  "z2":  "FF",
        "x2'": "RR",  "y2'": "UU",  "z2'": "FF",
    }
    palg = ''
    for m in salg.split(' '):
        palg += moves[m.strip('()')]
    return palg
    
def modelobj_from_string(modelstr):
    modelstr, mtype, sizes, exp = model.Model.from_string(modelstr)
    if exp is not None:
        raise ValueError('with-expression not allowed here')
    return model.Model(mtype, sizes)
    
    
class Shell (cmd.Cmd):
    intro = 'Welcome to the Pybik shell. Type help or ? to list commands.\n'
    prompt = '> '
    history_file = os.path.join(config.USER_CONFIG_DIR, 'shell-history')
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        model.Model.load_index()
        self.do_reset()
        
    # commands
    
    def do_reset(self, arg=None):
        '''reset to the initial state: reset'''
        self.model = modelobj_from_string('Cube 3')
        self.pattern = patterncube.PatternCube(self.model)
        
    def do_show(self, arg):
        '''show the current state: show'''
        print(self.model)
        print('pattern:', self.pattern)
        
    def do_quit(self, arg):
        '''exit Pybik shell: quit or Ctrl+D'''
        return True
        
    def do_model(self, arg):
        '''show or set current model: model [NEWMODEL]'''
        if arg:
            self.model = modelobj_from_string(arg)
        print(self.model)
        
    def do_singmaster(self, arg):
        '''convert singmaster moves to Pybik moves: singmaster NOTATION'''
        print(convert(arg))
        
    def do_posrot(self, arg):
        '''parse posrot notation of cube states: posrot NOTATION'''
        cube = cubestate.CubeState(self.model)
        if ':' not in arg:
            arg = 'idx-rot:' + arg
        cube.parse_block(arg)
        print('permutation:', [pos for pos, rot in cube._blocksn])
        print('rotations:  ', ', '.join(rot for pos, rot in cube._blocksn))
        pattern = patterncube.PatternCube(self.model)
        for pos, (idx, rot) in enumerate(cube._blocksr):
            cubie = patterncube.PatternCubie(self.model, pos)
            cubie.union(idx, rot)
            pattern[pos] = cubie
        print('pattern:', pattern)
        self.pattern = pattern
            
    def do_pattern(self, arg):
        '''parse patterns notation of cube states: pattern NOTATION'''
        pattern = patterncube.PatternCube(self.model)
        pattern.parse([c.split('=') for c in arg.split()])
        print('pattern:', pattern)
        self.pattern = pattern
        
    def do_move(self, arg):
        '''transform the current pattern: moves MOVECODE'''
        moves = MoveQueue()
        moves.parse(arg, len(arg), self.model)
        #print('moves:', moves.format(self.model)[0])
        for move in moves.moves:
            self.pattern = self.pattern.transform(move)
        print('pattern:', self.pattern)
        
    def do_match(self, arg):
        '''match the current pattern to an other pattern: match PATTERN'''
        condition = patterncube.PatternCube(self.model)
        condition.parse([c.split('=') for c in arg.split()])
        print('condition:', condition)
        accept, reject = self.pattern.fork(condition)
        print('accept:', accept)
        print('reject:', '\n        '.join(str(r) for r in reject))
        
    # ----------------
        
    def onecmd(self, line):
        cmd, arg, line = self.parseline(line)
        if not line:
            return self.emptyline()
        if cmd is None:
            return self.default(line)
        if line == 'EOF':
            print()
            return True
        self.lastcmd = line
        if cmd == '':
            return self.default(line)
        else:
            try:
                func = getattr(self, 'do_' + cmd)
            except AttributeError:
                return self.default(line)
            try:
                return func(arg)
            except Exception:
                sys.excepthook(*sys.exc_info())
                self.stdout.write('*** Error in: %s\n' % line)
                return None
                
    def preloop(self):
        with suppress(ImportError, FileNotFoundError):
            import readline
            readline.read_history_file(self.history_file)
            
    def postloop(self):
        with suppress(ImportError, OSError):
            import readline
            readline.write_history_file(self.history_file)
            
        
def main():
    Shell().cmdloop()
        
        
if __name__ == '__main__':
    main()
    

