import lib.operations as operations # basic variables concerning operations
from lib.schemas import * # related to the mathematical structure of the problem
from lib.textRepresentations import * # related to the propositions constituting the problem

from lib.subjectRepresentations import * # related to the problem state of a subject (representations and quantities)
from lib.paths import * # related to the solving process and its storing and analysis
from lib.dataManager import * # related to the final steps in the process gathering and printing results of simulation

"""
Explanation: (0) IMPORTS
we import all the python files needed
End of explanation
"""


schema1=Schema("PoissonEF","PoissonEI",operations.addition,"PoissonGAIN","change")
schema2=Schema("ViandeEF","ViandeEI",operations.addition,"ViandeGAIN","change")
print(schema1.objects)
print(schema2.objects)

"""
Explanation: [1] Create a problem
1.1 Declare the mathematical structure of the problem
Two schema describing the first relationships between the quantities are created
End of explanation
"""


struct=ProblemStructure()
struct.addSchema(schema1)
struct.addSchema(schema2)
struct.schemas

"""
Explanation: We then create the problem structure and add the schemas
End of explanation
"""


struct.addBridgingSchemas(schema1,schema2)

ls=struct.schemas
for schem in ls:
    print(schem.objects)

"""
Explanation: And we create the schemas in order to create all the correspondances between the first two schemas
End of explanation
"""


print(struct.objectSet)# before
struct.updateObjectSet() 
print(struct.objectSet) #after

print(struct.objectSet)

"""
Explanation: We then update the field corresponding to set of the object in the problem
End of explanation
"""


text=Text()

"""
Explanation: 1.2 Declare the text informations provided in the problem
The propositions
We create a text object
End of explanation
"""


t1='Au supermarché, le kilo de poisson a augmenté de 5 euros cette année'
t2='Un kilo de poisson coute maintenant 12 euros.'
t3='Au début de l\'année, le kilo de viande coutait le même prix que le kilo de poisson.'
t4='Le kilo de viande a augmenté de 3 euros de moins que le kilo de poisson'
q1=Representation(Quantity("PoissonGAIN","P1"),t1)
q2=Representation(Quantity("PoissonEF","T1"),t2)
q3=Representation(Quantity("PoissonEIminusViandeEI","dEI"),t3)
q4=Representation(Quantity("PoissonGAINminusViandeGAIN","d"),t4)
text.addTextInformation(TextInformation(q1))
text.addTextInformation(TextInformation(q2))
text.addTextInformation(TextInformation(q3))
text.addTextInformation(TextInformation(q4))

for info in text.textInformations:
    q=info.representations[0].quantity
    print(q.object, q.value)

"""
Explanation: And we add all the propositions, which are
End of explanation
"""


text.setGoal(TextGoal(Goal('ViandeEF','Combien coute le kilo de viande maintenant?')))

"""
Explanation: The Goal
End of explanation
"""


t1='Au supermarché, le kilo de poisson était de 5 euros'
t2='Au supermarché, le kilo de poisson coute 5 euros'
t3='Un kilo de poisson était de 12 euros.'
t4='Au la fin de l\'année, le kilo de viande coute le même prix que le kilo de poisson.'
t5='Le kilo de viande a augmenté du même prix que le kilo de poisson.'
t6='Le kilo de viande a augmenté de 3 euros'
t7='Le kilo de viande a diminué de 3 euros'
t8='Le kilo de viande vaut 3 euros de moins que le kilo de poisson'
t9='Le kilo coute 3 euros à la fin'

a1=Representation(Quantity("PoissonEI","P1"),t1)
a2=Representation(Quantity("PoissonEF","P1"),t2)
a3=Representation(Quantity("PoissonEI","T1"),t3)
a4=Representation(Quantity("PoissonEFminusViandeEF","dEI"),t4)
a5=Representation(Quantity("PoissonGAINminusViandeGAIN","dEI"),t5)
a6=Representation(Quantity("ViandeGAIN","d"),t6)
a7=Representation(Quantity("ViandeGAIN","-d"),t7)
a8=Representation(Quantity("PoissonEFminusViandeEF","d"),t8)
a9=Representation(Quantity("ViandeEF","d"),t9)

text.getTextInformation(0).addAlternativeRepresentation(a1)
text.getTextInformation(0).addAlternativeRepresentation(a2)
text.getTextInformation(1).addAlternativeRepresentation(a3)
text.getTextInformation(2).addAlternativeRepresentation(a4)
text.getTextInformation(2).addAlternativeRepresentation(a5)
text.getTextInformation(3).addAlternativeRepresentation(a6)
text.getTextInformation(3).addAlternativeRepresentation(a7)
text.getTextInformation(3).addAlternativeRepresentation(a8)
text.getTextInformation(3).addAlternativeRepresentation(a9)

"""
Explanation: The alternative representations
End of explanation
"""


textInfo=text.textInformations[3]
for representation in textInfo.representations:
    print(representation.quantity.object,representation.quantity.value)

"""
Explanation: Let's inspect the different alternative representations pertaining to the fourth text proposition
End of explanation
"""


probleme1=Problem(struct,text)

"""
Explanation: 1.3 final step to construct the problem
A problem is the association of a structure and a text as defined above
End of explanation
"""


probleme1.setInitialValues({"P1":5,"T1":12,"dEI":0,"d":3,"-d":-3})

"""
Explanation: Set the initial values
End of explanation
"""


upD=Updater(probleme1) # create the problem state
upD.startAsUnderstood() # init the problem state, we start with expert representations

"""
Explanation: [2] Handle solver object
2.1 Init its first problem representation
End of explanation
"""


probState=upD.problemState
print "quantity dic: ", probState.quantitiesDic.dic
print "\n representations: ",probState.representations

"""
Explanation: Updater has a central role in the program as it :
- Check if moves are possible
- Apply moves (schemas, reinterpretations) and then update its main attribute problemState which is also an important class
2.2 (side note) about the problem state

The quantitiesDic : current object-values association
representations which indicates which representations are currently selected
End of explanation
"""


c1=IntervalConstraint(['EF','EI'],operations.superiorOrEqualTo0) 

"""
Explanation: 2.3 Define the behavioral constraint of the solvers
End of explanation
"""


c2=BehavioralConstraint(breakTheOldOne=True)

"""
Explanation: This constraints implies that no negative values can be associated with object which has 'EI' or 'EF' as substring
End of explanation
"""


constraints=[c1,c2]

solver1=Solver(upD,constraints)

"""
Explanation: This constraints implies that if there is an alternative representation is selected, then the quantity formed by its prevous state is destroyed. In other terms, only one representation of the the same proposition is active at the same time.
2.4 Create the solvers (final step)
End of explanation
"""


solver1.updater.updateAppliableSchemas()
l=solver1.updater.appliableSchemaList
print(l)
print (l[0].objects)

"""
Explanation: 2.5 Manually run the solver
2.5.1 Apply a schema

check what are the schemas which can be applied.
End of explanation
"""


move1=Move(l[0]) # a move is one level more abastract than schema or representation
stepInformations1=solver1.updater.applyMove(move1,solver1.constraints) # we can then use the function applyMove for schemas or representation

"""
Explanation: apply a schema
End of explanation
"""


print(stepInformations1.formulaFirstPart,stepInformations1.valueToFind,stepInformations1.operands,stepInformations1.unknow)

"""
Explanation: stepInformations is an instance of InfoStep (in subjectRepresentations.py) and contains many informations related to the concrete application of the move, and can be used to inspect precisely a solution path.
Among others :
End of explanation
"""


solver1.updater.problemState.quantitiesDic.dic['PoissonEI']

"""
Explanation: In this specific case PoissonEI has been found knowing PoissonGAIN and PoissonEF.
A new quantity is then now available :
End of explanation
"""


solver1.updater.updatePossibleRepresentationChange()
print len(solver1.updater.possibleRepresentationChangeList), "representations change are possible"
print solver1.updater.problemState.representations, " is the current representations state"

repChange=solver1.updater.possibleRepresentationChangeList[5]
print "\nindex of the proposition in the text", repChange.indexTextInformation
print "index of the alternative representation of this proposition", repChange.indexSelectedRepresentation
rep=solver1.updater.problem.text.textInformations[repChange.indexTextInformation].representations[repChange.indexSelectedRepresentation]
print(rep.quantity.object, rep.quantity.value)

"""
Explanation: 2.5.2 Apply a representation change
informations on representations state and representations change
End of explanation
"""


move2=Move(repChange)
stepInformations2=solver1.updater.applyMove(move2,solver1.constraints)
print solver1.updater.problemState.representations, " is the new representations state"

"""
Explanation: Apply a representation change works broadly the same way as applying a Schema :
End of explanation
"""


solver1.updater.problemState.quantitiesDic.dic['ViandeGAIN']

"""
Explanation: Let's check that this new value is now in the quantitiesDic
End of explanation
"""


oldrep=solver1.updater.problem.text.textInformations[3].representations[0]
print "before : ",(oldrep.quantity.object, oldrep.quantity.value)
print "after : ", solver1.updater.problemState.quantitiesDic.dic[oldrep.quantity.object]

"""
Explanation: As we used the constraint c2=BehavioralConstraint(breakTheOldOne=True) we can see that the old representation of this text information is erased.
End of explanation
"""


step1=Step(move1,parentId=0, infos=stepInformations1,level=0)
step2=Step(move2,parentId=step1.sId, infos=stepInformations2, level=1) #we use the id of step1 to create step2

"""
Explanation: 2.5.3 Use steps classes to keep records of the path
Four classes are involved to keep such record : 
- StepInfo (explained above)
- Step : has step info as attribute but also 
    - An unique id
    - The ids of the step before him (father)
    - The ids of the steps after him (children)
- TreePaths : designed to store the steps, and the paths (as three and as lines).
- Path : give the essential informations of a path
2.5.3.1 steps
By convention the first step has for father the step with the id=0
End of explanation
"""


print "is the problem solved: ",step2.solved

"""
Explanation: We can check if the problem is solved by the solved attribute
End of explanation
"""


tp=TreePaths(solver1.updater) # or use the treePath instance tied to the solver : tp=solver1.TreePaths
tp.addStep(step1)
tp.addStep(step2)

"""
Explanation: Updater has no rollback feature for the moment, which means that 
2.5.3.2 TreePath
We can now add the step in the TreePaths
End of explanation
"""


id1=step1.childrenIds
id1

"""
Explanation: TreePath allows many things :
- Adding step to a treepaths bind the steps to its children
End of explanation
"""


tp.scanTree()
print(tp.treeOutput)

"""
Explanation: TreePaths has a method to generate a tree according the different paths taken
End of explanation
"""


print(tp.trackBack(step2.sId))

"""
Explanation: [Note] The last line gives a summary of what happenned. In this specific case we stopped the solving process without finding the solution. A more complex path would give such string : 
T1-d : interpretation -> PoissonEF-(PoissonGAINminusViandeGAIN+PoissonEIminusViandeEI)=ViandeEF 
This line is produced by the TreePath method trackBack which traceback the different paths and gives the last line. TrackBack is called in scanTree whenever a path is ended.
End of explanation
"""


firstPath=tp.pathList[0]
print(firstPath.formula)
print(firstPath.objectFormula)
print(firstPath.interpretationsSummary)
print(firstPath.valueFound)
print(firstPath.problemSolved)

"""
Explanation: trackBack is also usefull to store the different paths in lines which can be found in the pathList attribute of the updater.
End of explanation
"""


upD.startAsUnderstood()
autoSolver=Solver(upD,constraints)

"""
Explanation: 2.6 Automatic use of solver to generate a solution space
2.6.1 Simple example : only solve
2.6.1.1 solver.SOLVER instruction
We create a new solver from start
End of explanation
"""


l=[autoSolver.SOLVER]
autoSolver.generalSequentialSolver(listOfActions=l)

"""
Explanation: Now, we give the pattern of actions the solver has to explore
End of explanation
"""


autoSolver.TreePaths.scanTree()
print(autoSolver.TreePaths.treeOutput)

print(str(autoSolver.TreePaths.pathsCount)+" different paths have been discovered" )

"""
Explanation: This is the simplest example, the instruction solver.SOLVER means that schemas will be applied until the solution is found. At each step, the research tree has as many branches as schemas which can be applied.
2.6.1.2 inspect the solution space
We can now check what gives treeOutput and pathList with the solver process
End of explanation
"""


aPath=autoSolver.TreePaths.pathList[0]
print(aPath.formula)
print(aPath.objectFormula)
print(aPath.interpretationsSummary)
print(aPath.valueFound)
print(aPath.problemSolved)

"""
Explanation: Let's inspect the first path recorded in the treePaths
End of explanation
"""


upD.startAsUnderstood()
autoSolver2=Solver(upD,constraints)
autoSolver2.generalSequentialSolver(listOfActions=[autoSolver2.SCHEMA,autoSolver2.INTERP,autoSolver2.SOLVER])

"""
Explanation: 2.6.2 Other intructions
solver.SCHEMAS and solver.INTERP are two other instructions.
For example if the list of actions is [solver.SCHEM,solver.INTERP,solver.SOLVE], then the automatic solver will start with applying a schema, then applying a interpretation change, and finally will finish by a solving process as seen above. 
This list of instruction will find all the paths which has this kind of pattern.
End of explanation
"""


autoSolver2.TreePaths.scanTree()
aPath=autoSolver2.TreePaths.pathList[43]
print(aPath.formula)
print(aPath.objectFormula)
print(aPath.interpretationsSummary)
print(aPath.valueFound)
print(aPath.problemSolved)

"""
Explanation: Let's inspect a path
End of explanation
"""


simulatedDatas=SimulatedDatas()
simulatedDatas.addDataSet(solver1.TreePaths.pathList,"pbm1","Manual Solver")
simulatedDatas.addDataSet(autoSolver.TreePaths.pathList,"pbm1","Expert Solver")
simulatedDatas.addDataSet(autoSolver2.TreePaths.pathList,"pbm1","Another Solver")

"""
Explanation: [3] Handle the simulated datas
SimulatedDatas is a class used to store different treepath and eventually reduce some redondancies between the different paths.
End of explanation
"""


simulatedDatas.printLines()

"""
Explanation: simulatedDatas can print its datas in a CSV via simulatedDatas.printCSV() or in the console using simulatedDatas.printLines()
End of explanation
"""


