#!/usr/bin/python
## -*- coding: utf-8 -*-
## 
## (C) 2008, 2013 Muthiah Annamalai
## Licensed under GPL Version 3
## 
## This module is the pretty-printer for the Ezhil language.
## It contains classes @WikiStyle, @Printer
## 
from __future__ import print_function
from .theme import XsyTheme
from .ezhil_scanner import EzhilLex, EzhilToken
from .ezhil import EzhilInterpreter
from .transform import Visitor

class WikiStyle:
    @staticmethod
    def wrap_msg(attrib,text):
        """ @text is any character stream that has to be wrapper in a style-Theme,
            specified by @attrib """
        if ( len(attrib) >= 1 ):
            out = u'<span style="color:#'+attrib[0]
        if ( len(attrib) >= 2 ):
            out = out + u';background:#'+attrib[1]
        out = out + u'">' + str(attrib.process(text)) + u"</span>"
        return out

class Printer(Visitor):
    def __init__(self,src_file):
        """ @styler uses a Wiki/HTML/LaTeX output formatter, with a color theme 
        specificied by @theme to render the text into the appropriate format"""
        Visitor.__init__(self)
        self.styler = WikiStyle.wrap_msg
        self.theme = XsyTheme()
        self.lexer = EzhilLex(src_file)
        #print self.lexer.comments.keys()
        self.output = []
        self.line = 1 #current line 
        self.NEWLINE = "<BR />\n"
        
    def update_line(self,obj):
        #print obj.line,"= line"
        if ( obj.line > self.line ):
            self.line = obj.line
            self.append( self.NEWLINE  )
        if ( self.line in self.lexer.comments ):
            #print "visiting comment "
            self.append( self.lexer.comments[self.line] )
            del self.lexer.comments[self.line]
        return
    
    def append(self,string):
        self.output.append( string )
    
    def default(self,*args):
        """ /dev/zero dump for all visitor methods when not handled in derived class"""
        #args[0] is AST object
        self.append(u"def :")
        self.update_line(args[0])
        self.append((args[0]))
        
    def visit_identifier(self, IDobj):  
        attrib = self.theme.Variables
        self.update_line(IDobj)
        self.append( self.styler(attrib,(IDobj.id)))
        return
    
    def visit_string(self, string):
        attrib = self.theme.LiteralString
        self.update_line(string)
        self.append(self.styler(attrib,(string)))
        return

    def visit_number(self, num):
        attrib = self.theme.LiteralNumber
        self.update_line(num)
        self.append(self.styler(attrib,(num)))
        return

    def visit_expr_call(self,expr_call):
        var_attrib = self.theme.Variables
        self.update_line(expr_call)
        self.append(self.styler(var_attrib,(expr_call.func_id.id)))
        op_attrib = self.theme.Operators
        self.append( self.styler(op_attrib,"(") )
        expr_call.arglist.visit( self )
        self.append( self.styler(op_attrib,")") )
        return

    def visit_expr_list(self, expr_list):
        for pos,exp_itr in enumerate(expr_list.exprs):
            self.update_line(exp_itr)
            exp_itr.visit( self )
            if (pos+1) < len(expr_list.exprs):
                self.append(",")
            
        return
    
    def visit_stmt_list(self,stmt_list):
        for stmt in stmt_list.List:
            stmt.visit(self)
            self.update_line(stmt)
            self.append(self.NEWLINE)
        return
    
    def visit_stmt( self, stmt):
        ## is this a recipe for getting stuck in a loop?
        self.update_line(stmt)
        stmt.visit(self)
        return
    
    def visit_binary_expr(self,expr):
        self.visit_expr(expr)
        return
        
    def visit_expr(self, expr):
        op_attrib = self.theme.Operators
        self.update_line(expr)
        expr.term.visit(self)
        self.append( self.styler(op_attrib, EzhilToken.token_types[expr.binop.kind] ) )
        expr.next_expr.visit(self)
        return
    
    def visit_return_stmt(self, ret_stmt):
        kw_attrib = self.theme.Keywords
        self.update_line(ret_stmt)
        keyword = u"பின்கொடு"
        self.append( self.styler( kw_attrib, keyword ) )
        # return may have optional argument
        if hasattr(ret_stmt.rvalue,'visit'):
            ret_stmt.rvalue.visit(self)
        self.append(self.NEWLINE)
        return
    
    def visit_break_stmt(self, break_stmt ):
        kw_attrib = self.theme.Keywords
        self.update_line(break_stmt)
        keyword = u"நிறுத்து" #EzhilToken.Keywords["break"]
        self.append( self.NEWLINE )
        self.append( self.styler( kw_attrib, keyword ) )
        self.append( self.NEWLINE )
        return
    
    def visit_continue_stmt(self, cont_stmt):
        kw_attrib = self.theme.Keywords
        self.update_line(cont_stmt)
        keyword = u"தொடர்" #EzhilToken.Keywords["continue"]
        self.append( self.styler( kw_attrib, keyword ) )
        self.append( self.NEWLINE )        
        return
    
    def visit_else_stmt(self,else_stmt):
        kw_attrib = self.theme.Keywords
        keyword = u"இல்லை"
        self.append( self.styler( kw_attrib, keyword ) )
        self.update_line(else_stmt)
        else_stmt.stmt.visit( self )
        return
    
    def visit_if_elseif_stmt(self,if_elseif_stmt):
        op_attrib = self.theme.Operators
        
        # condition expression
        self.append( self.styler( op_attrib, "@( " ) )
        if_elseif_stmt.expr.visit(self)
        self.append( self.styler( op_attrib, ") " ) )
        
        # IF kw
        kw_attrib = self.theme.Keywords
        keyword_if = u"ஆனால்"
        self.append( self.styler( kw_attrib, keyword_if ) )                
        
        # True-Body
        if_elseif_stmt.body.visit( self )
        
        # False-Body - optionally present
        if hasattr(if_elseif_stmt.next_stmt,'visit'):
            if_elseif_stmt.next_stmt.visit(self)
        
        self.visit_end_kw()
        
    def visit_end_kw(self):
        # END kw
        kw_attrib = self.theme.Keywords
        keyword_end = u"முடி"
        self.append( self.styler( kw_attrib, keyword_end ) )
        
    def visit_while_stmt(self,while_stmt):
        """
        @( itr < L ) வரை
                        சமம்= சமம் + input[itr]*wts[itr]
            itr = itr + 1
                முடி"""        
        op_attrib = self.theme.Operators
        
        # condition expression
        self.append( self.styler( op_attrib, u"@( " ) )
        while_stmt.expr.visit(self)
        self.append( self.styler( op_attrib, u") " ) )
        
        # While kw
        kw_attrib = self.theme.Keywords
        keyword_while = u"வரை"
        self.append( self.styler(kw_attrib,(keyword_while)) )
        
        # Body
        while_stmt.body.visit( self )
        
        self.visit_end_kw()
        return
    
    # foreach is transformed at the AST-level
    # so its really a MACRO here
    def visit_for_stmt(self,for_stmt):
        """
        @( x = -1 , x < 0, x = x + 1 ) ஆக
                        பதிப்பி x, "கருவேபில"
                முடி
        """
        op_attrib = self.theme.Operators
        
        # condition expression
        self.append( self.styler( op_attrib, u"@( " ) )
        for_stmt.expr_init.visit(self)
        self.append( self.styler( op_attrib, u", " ) )
        for_stmt.expr_cond.visit(self)
        self.append( self.styler( op_attrib, u", " ) )
        for_stmt.expr_update.visit(self)
        self.append( self.styler( op_attrib, u") " ) )
        
        # For kw
        kw_attrib = self.theme.Keywords
        keyword_for = u"ஆக"
        self.append( self.styler(kw_attrib,keyword_for) )
        self.append( self.NEWLINE )
        # Body
        for_stmt.body.visit( self )
        self.visit_end_kw()
        return

    def visit_assign_stmt(self, assign_stmt):
        op_attrib = self.theme.Operators
        assign_stmt.lvalue.visit( self )
        self.append(self.styler(op_attrib,"="))        
        assign_stmt.rvalue.visit( self )
        return

    def visit_print_stmt(self, print_stmt):
        kw_attrib = self.theme.Keywords
        keyword = u"பதிப்பி"
        self.update_line(print_stmt)
        self.append( self.styler(kw_attrib,(keyword)) )
        print_stmt.exprlst.visit(self)
        return

    def visit_eval_stmt(self, eval_stmt ):
        eval_stmt.expr.visit(self)
        return
    
    def visit_arg_list(self, arg_list):
        L = len(arg_list.get_list())
        for pos,arg in enumerate(arg_list.get_list()):
            if hasattr(arg,'visit'):
                arg.visit(self)
            else:
                var_attrib = self.theme.Variables
                self.append( self.styler(var_attrib,(arg)) )            
            if ( pos < (L-1) ):
                self.append( ", " )
        return

    def visit_value_list(self,value_list):
        op_attrib = self.theme.Operators
        for value in value_list.args:
            self.update_line(value)
            value.visit(self)
            self.append(self.styler(op_attrib,","))
        self.output.pop()
        return

    def visit_function(self,fndecl_stmt):
        """
        நிரல்பாகம் fibonacci_தமிழ்( x )
        @( x <= 1 ) ஆனால்
                        ஈ = 1
                இல்லை
                        ஈ = fibonacci_தமிழ்( x - 1 ) + fibonacci_தமிழ்( x - 2 )
                முடி 
                பின்கொடு ஈ
        முடி
        """
        
        # Function kw
        kw_attrib = self.theme.Keywords
        keyword_fn = u"நிரல்பாகம்"
        self.append( self.styler(kw_attrib,keyword_fn) )
        
        # name of function
        fn_attrib = self.theme.Variables
        self.append( self.styler( fn_attrib, fndecl_stmt.name ) )
        
        # arglist expression
        op_attrib = self.theme.Operators
        self.append( self.styler( op_attrib, u"( " ) )
        fndecl_stmt.arglist.visit(self)
        self.append( self.styler( op_attrib, u") " ) )
        
        # Body expression
        fndecl_stmt.body.visit(self)
        
        return
    
    def pretty_print(self):
        self.parse_eval = EzhilInterpreter(lexer=self.lexer)
        ast = self.parse_eval.parse()
        print((ast))
        ast.visit(self)
        
        # dump remaining comments
        comm_attrib = self.theme.Comment
        for  line,comment in  self.lexer.comments.items():
            self.append( self.styler( comm_attrib, comment ) )
            self.append( self.NEWLINE )
        
        return u"".join(self.output)
        
    # method walks the lexer-tokens and calls the appropriate elements
    # basic lexical hiliting. This is useful when parsing fails, or just
    # a plain vanilla hiliter
    def lexical_hilite(self):
        self.lexer.tokens.reverse()
        out = []
        for t in self.lexer.tokens:
            add_br = False
            attrib = self.theme.Operators
            if EzhilToken.is_keyword(t.kind):
                attrib = self.theme.Keywords
                if ( EzhilToken.get_name(t.kind) in ["END", "ELSE"] ):
                    out.append(u'<BR />\n')
            elif EzhilToken.is_number(t.kind):
                attrib = self.theme.LiteralNumber
            elif EzhilToken.is_string(t.kind):
                attrib = self.theme.LiteralString
                t.val = u'"'+t.val+u'"' #FIXME: ideally do some escaping as well
            elif EzhilToken.is_id(t.kind):
                attrib = self.theme.Variables
            elif( t.val in [u"@", u"பதிப்பி" ] ):
                attrib = self.theme.Builtins
                out.append(u'<BR />\n')
            
            t.val = u" " + (t.val)
            out.append( self.styler(attrib,t.val) )
            if ( add_br ):
                out.append(u"<BR />\n")
        
        return u"".join(out)

def pretty_print_file( filename ):
    print(u"working with ",filename)
    print(Printer(filename).pretty_print())
    return True

if __name__ == "__main__":
    from sys import argv,exit
    if len(argv) <= 1:
        print(u"usage: python ezhil/prettify.py <file1> <file2> ... ")
        exit(-1)
    map( pretty_print_file, argv[1:])
    