class Block(object):
    sb=""
    sql=""
    indentLevel=-1
    def __init__(self,stage="",indentLevel=-1,sql=""):
        '''
        constructor for the class; generates the sql statements for a generic block
        @param stage:stage of the code block
        @type stage:string
        @param indentLevel: indent level of the statements
        @type indentLevel: int
        @param sql: sql statements in the blocl
        @type sql: string
        '''
        self.stage=stage
        self.indentLevel=indentLevel
        self.sb=""
        if(sql!="" and indentLevel!=-1):
            self.append(sql)
            self.sql=self.sb
        elif(sql!="" and indentLevel==-1):
            self.sql=sql
    def concat(self,stat):
        '''
        concatenates string to the existing sql statement
        @param stat: string to concat
        @type stat: string
        '''
        self.sb+=stat
    def append(self,stat,indent=-1):
        '''
        appends string to the existing sql 
        @param stat: string to append
        @type stat: string
        @param indent: indent level current
        @type indent: int
        '''
        if(indent==-1):
            self.append(stat,self.indentLevel)
        else:
            line=""
            for i in range(0,indent):
                line+=" "
            line+=stat
            self.sb+=line 
            self.sb+="\n"           
    def getStage(self):
        '''
        returs the stage
        '''
        return self.stage
    
    def getBlockSql(self):
        '''
        returns the sql statements
        '''
        return self.sql
    def getSql(self):
        '''
        returns the sql string buffer statements
        '''
        self.sql=self.sb
        return self.sql
    def getIndentLevel(self):
        '''
        returns the indent level
        '''
        return self.indentLevel
    def setStage(self,stage):
        '''
        sets the stage
        @param stage: string containing the stage of the block
        @type stage: string
        '''
        self.stage=stage
    def setSql(self,sql):
        '''
        sets the sql into the block
        @param sql: string containing sql
        @type sql: string
        '''
        self.sql=sql
    def setIndentLevel(self,indent):
        '''
        sets the indent level
        @param indent: indent level
        @type indent: int
        '''
        self.indentLevel=indent
    def display(self):
        '''
        displays the sql statements
        '''
        if(self.sql==""):
            self.sql=self.sb
        print self.sql                     class CreateTableBlock(Block):
    '''
    generates the create table sql statement
    '''
    
    def __init__(self,stage,indent,tableName,attrs):
        '''
        constructor: generates a create table sql
        @param stage:current stage
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param tableName: Name of table to create
        @type tableName: string
        @param attrs: attribute list to have in the table
        @type attrs: list
        '''
        super(CreateTableBlock,self).__init__(stage,indent)
        self.append("CREATE TABLE " + tableName + "(")
        ind=0
        for attr in attrs:
            if(ind==len(attrs)-1):
                self.append(attr,indent+1)
            else:    
                self.append(attr+",",indent+1)  
            ind=ind+1      
        self.append(")" + ";")   
        self.sql=self.sb  class DropIndexBlock(Block):
    '''
    generates sql to drop an index on a table
    '''
    
    def __init__(self,stage,indent,index,tbName):
        '''
        constructor: 
        @param stage: stage of the block 
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param index: index to drop
        @type index: string
        @param tbName: table name 
        @type tbName: string
        '''
        super(DropIndexBlock,self).__init__(stage,indent)
        self.append("DROP INDEX IF EXISTS " + str(index) + ";")
        self.sql=self.sb class DropTableBlock(Block):
    '''
    generates sql to drop a table 
    '''
    
    def __init__(self,stage,indent,tbName):
        '''
        constructor: generates sql to drop table
        @param stage: stage of the block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param tbName: Name of table to drop
        @type tbName: string
        '''
        super(DropTableBlock,self).__init__(stage,indent)
        self.append("DROP TABLE IF EXISTS "+tbName+";")
        self.sql=self.sb class DeclareBlock(Block):
    '''
    Generates declaration statements
    '''
    def __init__(self,stage,indent, declareString):
        '''
        constructor
        @param stage: stage of the block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param declareString: declaration statements separated by \n
        @type declareString: string
        '''
        super(DeclareBlock,self).__init__(stage,indent)
        self.append("DO $$")
        self.append("DECLARE"+"\n")
        self.append(declareString)
        self.sql=self.sb  class InsertBlock(Block):
    '''
    generates sql for an insert block
    '''
    
    def __init__(self,stage,indent,attr,targetTb,fromTb,pred=""):
        '''
        constructor: generates insert into sql statements
        @param stage: stage of the block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param attr: attribute list to select from
        @type attr: list
        @param targetTb:table name of the target table
        @type targetTb: string
        @param fromTb: table name of the from table
        @type fromTb: string
        '''
        super(InsertBlock,self).__init__(stage,indent)
        self.append("INSERT INTO " + targetTb)
        self.append("(SELECT " + attr)
        self.append("FROM " + fromTb )
        tail = ""
        if(len(pred) > 0):
            tail = "WHERE "+pred+" "
        tail = tail + ");"
        self.append(tail)
        self.sql = self.sb class SelectIntoBlock(Block):
    '''
    Generates the Select statements that union/intersect 2 sql queries
    '''
    
    def __init__(self,stage,indent,attrs,targetTb,lhs,rhs,op):
        '''
        constructor: constructs that generates sql statement like union/intersect
        @param stage: stage of the block
        @type stage: current stage
        @param indent: indent level
        @type indent: int
        @param attrs: attributes list to select into
        @type attrs: list
        @param targetTb: target table name
        @type targetTb: string
        @param lhs: sql to generate  the left table
        @type lhs: string
        @param rhs: sql to generate the right table
        @type rhs: string
        @param op: operation on the 2 tables
        @type op: string
        '''
        super(SelectIntoBlock,self).__init__(stage,indent)
        line=""
        if (targetTb != ""):
            line+="DROP TABLE IF EXISTS "+targetTb+";"
            line+="\n CREATE TABLE "+targetTb+" AS"
        line+="\nSELECT "
        for i in range(0,len(attrs)-1):
            line=line+attrs[i]+", "
        line+=attrs[len(attrs)-1]
        self.append(line)
        self.append("FROM (");
        templhs=lhs.getSql()
        templhs=templhs[0:templhs.rfind(";")]
        temprhs=rhs.getSql()
        temprhs=temprhs[0:temprhs.rfind(";")]
        self.concat(templhs);
        self.append(op, indent + 1);
        self.concat(temprhs);
        self.append(") AS Temp");
	self.append(";")
        self.sql = self.sb class SelectIntoExtended(Block):
    '''
    The SQL block for select into statement. Typically, the form is
    SELECT a AS temp1, b AS temp2, c....
    FROM var1,var2
    WHERE <condition>
    '''
    def __init__(self,stage,indent,attrs,targetTb,fromList,pred,joinWithId,groupBy):
        '''
        constructor
        @param stage: stage of the  block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param attrs: attribute list to project
        @type attrs: list
        @param targetTb: target table name (select into <>)
        @type targetTb: string
        @param fromList: list of tables to select from
        @type fromList: list
        @param pred: conditions/predicates used for selection
        @type pred: string
        @param joinWithId: indicates whether to join with the common vertex id or not
        @type joinWithId: boolean/int
        @param groupBy: attribute on which we should group result on
        @type groupBy: string
        '''
        super(SelectIntoExtended,self).__init__(stage,indent)
        line=""
        if (targetTb != ""):
            line+="DROP TABLE IF EXISTS "+targetTb+";"
            line+="\n CREATE TABLE "+targetTb+" AS"
        line+="\nSELECT "
        for i in range(0,len(attrs)-1):
            line=line+attrs[i]+", "
        line=line+attrs[len(attrs)-1]
        self.append(line)
        line=""
        for i in range(0,len(fromList)-1):
            line=line+fromList[i]+", "
        line+=fromList[len(fromList)-1]
      
        self.append("FROM " + line)
        line=""
        
        if (joinWithId):
            line+="WHERE "


            for i in range(0,len(fromList)-1):
                if(fromList[i]=="edge" or fromList[i+1]=="edge"):
                    continue
            	if(i==0):
                	line=line+fromList[i]+".id = "+fromList[i+1]+".id "
            	else:
                	line=line+"AND " + fromList[i] + ".id = " + fromList.get[i+1]+ ".id "              
            ''' 
            if(len(fromList)>2 and fromList[len(fromList)-2]!="edge" and fromList[len(fromList)-1]=="edge"):
                if(len(fromList)==2):
                    line=line+fromList[len(fromList)-2]+ ".id = " + fromList[len(fromList) - 1] + ".id"
                else:
		    col = ".id"
		    if groupBy == "src":
			col = ".dest"
		    else:
			col = ".src"	
                    line=line+" AND " + fromList[len(fromList) - 2]+ ".id = " + fromList[len(fromList) - 1] + col
            '''       
            if(pred != ""):
                if(len(line)==6):
                    line+=pred
                else:
                    line=line+" AND "+pred    
        else:
            if(pred != ""):
                line=line+"WHERE "+pred
        
        if(len(line)!=0):
            self.append(line)
                  
        if(groupBy != "" and targetTb != 'message'):
            self.append("GROUP BY "+groupBy)
        self.append(";")
        self.sql=self.sb class UpdateVertexBlock(Block):
    '''
    generates sql statements to update the value of the vertices
    '''
    
    otherTable=""
    valueExpression="" 
    def getValueExpression(self):
        '''
        returns the scalar function/value expression
        '''
        return self.valueExpression
    def getOtherTable(self):
        '''
        returns the name of the other table
        '''
        return self.otherTable

    def __init__(self,stage,indent,col_dict,from_lst,pred=""):
        '''
        constructor
        @param stage: stage of the block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param otherTable: name of the other table from which new values are retrieved
        @type otherTable: string
        @param valueExpression: expression or scalar function of the attribute in the other table
        @type valueExpression: string
        '''
        super(UpdateVertexBlock,self).__init__(stage,indent)
	'''
        self.otherTable = otherTable;
        self.valueExpression = valueExpression;
        self.append("UPDATE next SET val = " + valueExpression);
        self.append("FROM "+otherTable);
        self.append("WHERE next.id = " + otherTable + ".id" + ";");
        self.sql = self.sb

  	self.update_next(col_dict,from_lst) 
	'''

	self.append("UPDATE next SET ")
	keys = col_dict.keys()
	for i in range(0,len(keys)-1):
		self.append(keys[i]+"="+col_dict[keys[i]]+",")
	self.append(keys[len(keys)-1]+"="+col_dict[keys[len(keys)-1]])
	self.append("FROM ")
	for i in range(0,len(from_lst)-1):
		self.append(from_lst[i]+",")
	self.append(from_lst[len(from_lst)-1])
	self.append("WHERE ")
	for i in range(0,len(from_lst)-1):
		self.append("next.id="+from_lst[i]+".id and ")
	self.append("next.id="+from_lst[len(from_lst)-1]+".id")
	if(pred != ""):
		self.append("and "+pred)
	self.append(";")

	self.sql = self.sb	 class BeginWhileBlock(Block):
    '''
    Generates sql statements for a begin while block
    '''
    def __init__(self,stage,indent, loopCondition):
        '''
        constructor
        @param stage: current stage  
        @type stage: string 
        @param indent: indent level
        @type indent: int
        @param loopCondition: condition inside the while loop
        @type loopCondition: string
        '''
        super(BeginWhileBlock,self).__init__(stage,indent)
        self.append("BEGIN"+"\n")
        self.append("WHILE "+loopCondition+" LOOP")
        self.sql=self.sb  class CreateandInitSeqBlock(Block):
    '''
    generates the create table sql statement
    '''
    
    def __init__(self,stage,indent,seq_tokens):
        '''
        constructor: generates a create table sql
        @param stage:current stage
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param tableName: Name of table to create
        @type tableName: string
        @param attrs: attribute list to have in the table
        @type attrs: list
        '''
        
	super(CreateandInitSeqBlock,self).__init__(stage,indent)
	self.append("DROP SEQUENCE IF EXISTS "+ seq_tokens[0] + ";")
        self.append("CREATE SEQUENCE " + seq_tokens[0] + " MINVALUE "+seq_tokens[1] + ";")
        self.append("SELECT setval(" + seq_tokens[2]+ "," + seq_tokens[3] + ");")   
        self.sql=self.sb  class EndWhileBlock(Block):
    '''
    generates sql to end a while loop
    '''
    
    endStr=""
    
    def getEndStr(self):
        '''
        returns the ending string
        '''
        return self.endStr
    def __init__(self,stage,indent,endStr,counter):
        '''
        constructor: generates the sql for end while
        @param stage: stage of the block
        @type stage: string
        @param indent: indent level
        @type indent: int
        @param endStr: end string 
        @type endStr: string
        @param counter: table name thats used to decide the number of iterations
        @type counter: string
        '''
        super(EndWhileBlock,self).__init__(stage,indent)
        self.endStr=endStr
        if(self.endStr=="NO_MESSAGE"): 
            self.append("flag := (SELECT COUNT (*) FROM " + str(counter) + ");")
        else:
            self.append("flag := flag - 1;") 
        self.append("END LOOP;",indent-1)
        self.append("\nEND $$;")
        self.sql=self.sb             class TbNameGen:
    '''
    Class that's used to generate the next available table name. Starts from Table1, Table2 and so on
    '''
    curIdx=0  #static variable
    def getNextTbName(self):
        '''
        returns the next available table name
        '''
        TbNameGen.curIdx=TbNameGen.curIdx+1
        return "Table"+ str(TbNameGen.curIdx-1); class CommonDefs:
    '''
    Class containing some commonly used values
    '''
    INT_MAX = "2147483647"
    INT_MIN = "-2147483648"
    DBL_MAX = "1.79E+308"
    DBL_MIN = "1.79E+308"
from Blocks.Block import Block
from Blocks.CreateTableBlock import CreateTableBlock
from Blocks.DropIndexBlock import DropIndexBlock
from Blocks.DropTableBlock import DropTableBlock
from Blocks.DeclareBlock import DeclareBlock
from Blocks.InsertBlock import InsertBlock
from Blocks.SelectIntoBlock import SelectIntoBlock
from Blocks.SelectIntoExtended import SelectIntoExtended
from Blocks.UpdateVertexBlock import UpdateVertexBlock
from Blocks.BeginWhileBlock import BeginWhileBlock
from Blocks.CreateandInitSeqBlock import CreateandInitSeqBlock
from Blocks.EndWhileBlock import EndWhileBlock
from collections import OrderedDict
from TbNameGen import TbNameGen
from CommonDefs import CommonDefs
import re
			col_name_val_pairs = stat[:from_indx].split(",")
			
		else:
			tbl_list.append(context)
			if(stat.find(",") >= 0):   # != stat.rfind(",")): #multiple columns to be updated
				col_name_val_pairs=stat.split(",")	
		i = 0
		for entry in col_name_val_pairs:
        		if(i%2 == 0 and i <= len(col_name_val_pairs)-2):
                		s1 = col_name_val_pairs[i]
                		s2 = col_name_val_pairs[i+1]
                		col_lst[s1]=s2
        		i = i + 1
		if(len(col_name_val_pairs) == 0):
				col_lst['val'] = newVal		

		
		pred_indx = stat.find("CONDN")
		pred = ""
		if(pred_indx > 0):
			condn = stat[pred_indx+6:]
			pred = condn[0:condn.find(")")]	

		#update statement for next table with column list and from table list and join on id		
                self.blocks.append(UpdateVertexBlock("setVal",
                                                     self.indentLevel,
						     col_lst,
						     tbl_list,		
						     pred))


                newVal = newVal.replace("cur", "next")
                self.convertedOptions["setValContext"] = context
                self.convertedOptions["setValNewVal"] = newVal
                if (context == "cur"):
                    self.convertedOptions["isSender"] = "all"
                else:
                    self.convertedOptions["isSender"] = "notAll"

            elif (self.getStatementType(stat) == self.SEND_MSG):
                #params = stat[stat.index("(") + 1:stat.rfind(')')].split(",")
                newVal = stat[stat.index("(")+1:stat.rfind(')')]

                if(newVal.startswith("JOIN")):
                    params=[]
                    close_paren_indx = newVal.index(')')

                    params.append(newVal[0:close_paren_indx+1])
                    params.append(newVal[close_paren_indx+2:]) #ignore comma after close parens
                    self.convertedOptions["msgDir"] = "MATCH"
                    context=""
                    self.sendMsg(params[0], params[1], context)
                    self.convertedOptions["SendMsgDir"] = "MATCH"
                else:
                    params = newVal.split(",")
                    for j in range(0, len(params)):
                        params[j] = params[j].strip()  # TODO
                    self.convertedOptions["msgDir"] = params[0]
                    params[1] = params[1].replace("getVal()", "next.val")
                    params[1] = params[1].replace("getAggregationVal()", context + ".val")
		    predicate = ""	
		    if len(params) == 3:  #predicate found
			predicate = params[2]	
                    self.sendMsg(params[0], params[1], context, predicate)
                    self.convertedOptions["SendMsgDir"] = params[0]

            elif (self.getStatementType(stat) == self.END_IF):
                if (len(contexts) > 0):
                    contexts = contexts[:-1]

            elif (self.getStatementType(stat) == self.ASSIGNMENT):
                self.genTbForVar(context, stat)

    def dropTable(self, s):
        '''
        generates block for dropping a table
        @param s:table name
        @type s: string
        '''
        self.blocks.append(DropTableBlock("drop" + s, self.indentLevel, s))

    def superstep(self):
        '''
        invokes a list of functions in each iteration
        '''
        self.combineMsg()
        self.dropTable("message")
        self.tableNameList.remove("message")
        self.updateAndSend()

    def getColNames(self, tablename):
        if (tablename == 'next'):
	    if self.next_tbl_schema == "" :
            	colNames = ["id", "val"]
	    else:
		colNames = self.next_tbl_schema.split(",")
	    #["id", "val", "aggMsgCnt"]
        else:
            colNames = []
        return colNames

    def copyVertex(self):
        '''
        generate block to copy data from the vertex table
        '''
        colNames = self.getColNames('next')
        if (len(colNames) > 0):
            attrList = []
            attrList.append("id AS " + colNames[0])
            fromList = []
            fromList.append("vertex")

            initValues = self.options['InitiateVal']
            initV = initValues.split(",")
            i = 1
            for initVal in initV:
                if (initVal == "INT_MAX"):
                    initVal = CommonDefs.INT_MAX

                elif (initVal == "INT_MIN"):

                    initVal = CommonDefs.INT_MIN