#!/usr/bin/env python
from bokeh.plotting import figure, output_file, show
from cbd.src.CBD import *
import math
        
class CircleCBDIntegrator(CBD):
    def __init__(self, delta_t):
        CBD.__init__(self,"HarmonicOscilatorIntegrator", [], [])
        self.delta=delta_t;
        self.addBlock(ConstantBlock(block_name="one", value=1.0))
        self.addBlock(ConstantBlock(block_name="zero", value=0.0))
        self.addBlock(ConstantBlock(block_name="delta", value=self.delta))
        self.addBlock(NegatorBlock(block_name="neg"))
        self.addBlock(IntegratorBlock(block_name="intx"))
        self.addBlock(IntegratorBlock(block_name="intv"))
        
        self.addConnection("delta", "intx",  input_port_name="delta_t")
        self.addConnection("delta", "intv",  input_port_name="delta_t")
        self.addConnection("zero", "intx",  input_port_name="IC")
        self.addConnection("one", "intv",  input_port_name="IC")
        self.addConnection("intx", "neg")
        self.addConnection("neg", "intv")
        self.addConnection("intv", "intx")
        
        # error measurement blocks
        self.addBlock(IntegratorBlock(block_name="inte"))
        self.addBlock(NegatorBlock(block_name="negX"))
        self.addBlock(AdderBlock(block_name="addNeg"))
        self.addBlock(ABSBlock(block_name="absError"))
        self.addBlock(SinBlock(block_name="sin"))
        self.addConnection("zero", "inte",  input_port_name="IC")
        self.addConnection("delta", "inte",  input_port_name="delta_t")
        self.addConnection("intx", "negX")
        self.addConnection("negX", "addNeg")
        self.addConnection("sin", "addNeg")
        self.addConnection("addNeg", "absError")
        self.addConnection("absError", "inte", input_port_name="IN1")
        


class SinBlock(BaseBlock):
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, [], ["OUT1"])

    def compute(self, curIteration):
        self.appendToSignal(math.sin(self.getClock().getTime()))
        
     

class CircleCBDDerivative(CBD):
    def __init__(self, delta_t):
        CBD.__init__(self,"HarmonicOscilatorDerivative", [], [])
        self.delta=delta_t;
        self.addBlock(ConstantBlock(block_name="one", value=1.0))
        self.addBlock(ConstantBlock(block_name="zero", value=0.0))
        self.addBlock(ConstantBlock(block_name="delta", value=self.delta))
        self.addBlock(NegatorBlock(block_name="neg"))
        self.addBlock(DerivatorBlock(block_name="derxv"))
        self.addBlock(DerivatorBlock(block_name="derva"))
        
        self.addConnection("delta", "derxv",  input_port_name="delta_t")
        self.addConnection("delta", "derva",  input_port_name="delta_t")
        self.addConnection("zero", "derva",  input_port_name="IC")
        self.addConnection("one", "derxv",  input_port_name="IC")
        self.addConnection("derva", "neg")
        self.addConnection("neg", "derxv")
        self.addConnection("derxv", "derva")
        
        # error measuring blocks 
        self.addBlock(IntegratorBlock(block_name="inte"))
        self.addBlock(NegatorBlock(block_name="negX"))
        self.addBlock(AdderBlock(block_name="addNeg"))
        self.addBlock(ABSBlock(block_name="absError"))
        self.addBlock(SinBlock(block_name="sin"))
        self.addConnection("zero", "inte",  input_port_name="IC")
        self.addConnection("delta", "inte",  input_port_name="delta_t")
        self.addConnection("neg", "negX")
        self.addConnection("negX", "addNeg")
        self.addConnection("sin", "addNeg")
        self.addConnection("addNeg", "absError")
        self.addConnection("absError", "inte", input_port_name="IN1")


def plotTime(cbd, blockToInspect, fileName, title, label):
    
    times = []
    values = []
    
    for timeValuePair in cbd.getBlockByName(blockToInspect).getSignal():
        times.append(timeValuePair.time)
        values.append(timeValuePair.value)
    
    #Output solution and numerical approximation
    output_file("./"+fileName+".html", title=title)
    p = figure(title=title, x_axis_label='time', y_axis_label=label)
    p.line(times, values, legend=label, line_width=1, line_color="red")
    
    show(p)

def plotError(cbd, fileName, title):
    
    times = []
    values = []
    
    for timeValuePair in cbd.getBlockByName("inte").getSignal():
        times.append(timeValuePair.time)
        values.append(timeValuePair.value)
    
    #Output solution and numerical approximation
    output_file("./"+fileName+".html", title=title)
    p = figure(title=title, x_axis_label='time', y_axis_label="error")
    p.line(times, values, legend="error", line_width=1, line_color="red")
    show(p)

def plotParametric(cbd, blockToInspectHorAxis, blockToInspectVerAxis, fileName, title, horLabel, verLabel):
    
    times = []
    values = []
    
    for timeValuePair in cbd.getBlockByName(blockToInspectHorAxis).getSignal():
        times.append(timeValuePair.value)
    
    for timeValuePair in cbd.getBlockByName(blockToInspectVerAxis).getSignal():
        values.append(timeValuePair.value)
    
    #Output solution and numerical approximation
    output_file("./"+fileName+".html", title=title)
    p = figure(title=title, x_axis_label=horLabel, y_axis_label=verLabel)
    p.line(times, values, legend=horLabel + " vs " + verLabel, line_width=1, line_color="red")
    show(p)

def getErrorSin(cbd, blockThatGivesSin):
    error = 0.0
    fullSignal = cbd.getBlockByName(blockThatGivesSin).getSignal()
    for timeValuePair in fullSignal:
        error += abs(timeValuePair.value - math.sin(timeValuePair.time))
    return error/len(fullSignal)

NUM_STEPS = 100

delta = 0.1
cbdInt = CircleCBDIntegrator(delta)
cbdInt.run(int(NUM_STEPS/delta), delta_t = delta)    

times = []
values = []
valuesSin = []

for timeValuePair in cbdInt.getBlockByName("intx").getSignal():
    times.append(timeValuePair.time)
    values.append(timeValuePair.value)
    valuesSin.append(math.sin(timeValuePair.time))

#Output solution and numerical approximation
output_file("./plots.html", title="title")
p = figure(title="", x_axis_label='time', y_axis_label="asas")
p.line(times, values, legend="asas", line_width=1, line_color="red")
p.line(times, valuesSin, legend="eeeee", line_width=1, line_color="blue")
show(p)

'''
#General plotting
for delta in [0.1, 0.01 , 0.001, 0.0001]:
    cbdInt = CircleCBDIntegrator(delta)
    cbdInt.run(int(NUM_STEPS/delta), delta_t = delta)
    plotTime(cbdInt, "intx", "circle_integrator_delta"+str(delta), "Harmonic Oscillator with integrators and delta " + str(delta), "position (x)")
    plotParametric(cbdInt, "intx", "intv", "circle_integrator_parametric_delta"+str(delta), "Circle with integrators and delta " + str(delta), "x", "y")
    plotError(cbdInt, "circle_integrator_delta"+str(delta)+"_error", "Error of Harmonic Oscillator with integrators and delta " + str(delta))
    
    cbdDer = CircleCBDDerivative(delta)
    cbdDer.run(int(NUM_STEPS/delta), delta_t = delta)
    plotTime(cbdDer, "neg", "circle_derivative_delta"+str(delta), "Harmonic Oscillator with derivatives and delta " + str(delta), "position (x)")
    plotParametric(cbdDer, "neg", "derxv", "circle_derivative_parametric_delta"+str(delta), "Circle test with derivatives and delta " + str(delta), "x", "y")
    plotError(cbdDer, "circle_derivative_delta"+str(delta)+"_error", "Error of Harmonic Oscillator with derivatives and delta " + str(delta))
'''

'''
# Error plotting
MAX_TIME_ERROR = 10
errorValuesInt = []
errorValuesDer = []
#deltas = map((lambda x: 10 ** (-x)),range(5))
deltas = map((lambda x: (x+1)/10000.0),range(10000))
for delta in deltas:
    cbdInt = CircleCBDIntegrator(delta)
    cbdInt.run(int(MAX_TIME_ERROR/delta), delta_t = delta)
    errorValuesInt.append(getErrorSin(cbdInt, "intx"))
    
    cbdDer = CircleCBDDerivative(delta)
    cbdDer.run(int(MAX_TIME_ERROR/delta), delta_t = delta)
    errorValuesDer.append(getErrorSin(cbdDer, "neg"))


output_file("./error_vs_delta.html", title="Error vs Step Size")
p = figure(title="Error vs Step Size", x_axis_label="Step size", y_axis_label="Error")
p.line(deltas, errorValuesInt, legend="Integrator", line_width=1, line_color="red")
p.line(deltas, errorValuesDer, legend="Derivative", line_width=1, line_color="blue")
show(p)
'''







