##########################################################
# proTyres common utilities lib by Jens Roos (Wergilius at AC forum)
#
# 2.0.0 200528
# 
# 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.
#
# For GNU General Public License see <http://www.gnu.org/licenses/>.
######################################################################

import ac
import math
from lib.proTyres_def import *
from bisect import bisect_left
     
#logg value to console and py_log.txt
def ptLog(Msg,vl):
  if vl:
    ac.console(Msg)
    ac.log(Msg)

#glQuad with RGBA color and related value
class iBox:
  def __init__(self):
    self.x = 0
    self.y = 0
    self.w = 0
    self.h = 0
    self.Color = [0,0,0,0]
    self.Value = 0                      #actual value or size 0-1
    self.raw = 0                        #always raw value if not availible from value
    self.idx = 0                        #size of vertex array
    self.vertex=[]

class iBoxM:
  def __init__(self):
    self.x = 0
    self.y = 0
    self.w = 0
    self.h = 0
    self.Color = [0,0,0,0]

class iBoxMi:
  def __init__(self):
    self.x = 0
    self.y = 0
    self.w = 0
    self.h = 0
    self.Color = [0,0,0,0]
    self.idx = 0
  
#draw a glQuad, X,Y cordinate is referenced to left bottom
def drawBar(c,x,y,w,h):
  ac.glColor4f(*c)
  #ac.console(">>: b{},{},{},{}".format(x,y,w,h))
  ac.glQuad(x, y-h, w, h)

#draw rectangular quad with vertex reference, must be ccw
def drawQuad(p1,p2,p3,p4):
  ac.glVertex2f(*p1)
  ac.glVertex2f(*p2)
  ac.glVertex2f(*p3)
  ac.glVertex2f(*p4)

#draw multi quads from preset vertex arrays
def drawBarR(b):
  #ac.console(str(b.vertex[0][0])+" "+str(b.vertex[0][1])+" "+str(b.vertex[0][2])+" "+str(b.vertex[0][3]))
  s=int(b.idx/2)       #number of shaders
  ac.glBegin(3)
  for i in range(0,b.idx):
    #first half, if any, is shades
    #if i==0 and s>0:
    #  ac.glColor4f(b.Color[0],b.Color[1],b.Color[2],b.Color[3]*0.2)
    if i<s:
      ac.glColor4f(b.Color[0],b.Color[1],b.Color[2],b.Color[3]*0.25)
    else:
      ac.glColor4f(*(b.Color))
    #vertex array is already calculated
    drawQuad(*(b.vertex[i]))
  ac.glEnd()   

#calculate vertex array, x,y is bottom left of expected recangle/romb
def setDrawBarR(cfg,b,useR,shape):
  #not any radius, create rectangle
  if not useR or not shape:
    b.vertex = [[[b.x,b.y],[b.x+b.w,b.y],[b.x+b.w,b.y-b.h],[b.x,b.y-b.h]]]
  #use radius, creacte arrays with rectangle/rombs
  else:
    #a=sa*3 (3:e segmentent)
    #x=math.cos(a)*6 (pos x,y, från vänster till höger upp)
    #y=math.sin(a)*6

    #set radius and elements, may not be bigger than height-1
    r = min(float(cfg.sizeRadius),float(b.h-1))
    e = min(float(cfg.resRadius),float(b.h-1))
    #set segment angle for each part of the 90 degree radius
    sa = math.pi/(180/(90/e))
    #clear array/indexes
    b.vertex = []
    h = 0
    n = 1
    rs = 0
    ls = 0
    #reverse height value if bottom position
    if shape&RAD_BOTTOM:
      h = b.h
      n=-1
    #shade for left/right radius
    if not shape&RAD_RIGHT:
      rs = -1
    if not shape&RAD_LEFT:
      ls = 1

    #init start pos for left/right and Y base
    Yold = b.y-b.h+h+int(r)*n
    lX = lXold = b.x
    rX = rXold = b.x+b.w
    
    #create radius elements
    for i in range(1,int(e)+1):
      a = sa*i                  #angle, in radians
      x = int(r-round(math.cos(a)*r))    #x position
      y = int(r-round(math.sin(a)*r))    #y position
      if shape&RAD_LEFT:
        lX = b.x+x+1
      if shape&RAD_RIGHT:
        rX = b.x+b.w-x
      Y = b.y-b.h+h+y*n
      #create base shape
      b.vertex.append([[lXold,Yold],[rXold,Yold],[rX,Y],[lX,Y]])
      ys = 0
      #last element, create out shader
      if i==int(e):
        ys = 1
      #create shader
      b.vertex.insert(0,[[lXold-1+ls,Yold],[rXold+1+rs,Yold],[rX+1+rs,Y-ys*n],[lX-1+ls,Y-ys*n]])
      #save previous position
      Yold = Y
      lXold = lX
      rXold = rX
      #if radius is at the bottom, reverse the list for cc cordinates
      if h:
        b.vertex[len(b.vertex)-1] = b.vertex[len(b.vertex)-1][::-1]
        b.vertex[0] = b.vertex[0][::-1]

    #create base, radius should always be smaller than height
    #if r<b.h:
    b.vertex.append([[b.x,b.y-h],[b.x+b.w,b.y-h],[b.x+b.w,b.y-b.h+h+int(r)*n],[b.x,b.y-b.h+h+int(r)*n]])
    #if radius is at the bottom, reverse the list for cc cordinates
    if h:
      b.vertex[len(b.vertex)-1] = b.vertex[len(b.vertex)-1][::-1]        

  #create shades for last created vertex, if shape is set
  if cfg.useRadius and shape>=ALPHA_TOP:
    #create base ordinates
    i=len(b.vertex)-1
    x = [b.vertex[i][0][0],b.vertex[i][1][0],b.vertex[i][2][0],b.vertex[i][3][0]]
    y = [b.vertex[i][0][1],b.vertex[i][1][1],b.vertex[i][2][1],b.vertex[i][3][1]]
    #stretch the shape if selected
    if shape&ALPHA_TOP:
      y[2]-=1
      y[3]-=1
    if shape&ALPHA_BOTTOM:
      y[0]+=1
      y[1]+=1
    if shape&ALPHA_LEFT:
      x[0]-=1
      x[3]-=1
    if shape&ALPHA_RIGHT:
      x[1]+=1
      x[2]+=1
    #create shade vertex array
    b.vertex.insert(0,[[x[0],y[0]],[x[1],y[1]],[x[2],y[2]],[x[3],y[3]]])
  
  #set size of vertex array
  b.idx=len(b.vertex)
  
#draw a rectangle, X,Y cordinate is referenced to left bottom
#border of rectangle is on the inside of same size glQuad
def drawRectangle(c,x,y,w,h):
  ac.glColor4f(*c)
  #ac.console(">>: r{},{},{},{}".format(x,y,w,h))
  x += 1
  w -= 1
  h -= 1
  ac.glBegin(1)
  ac.glVertex2f(x,y)
  ac.glVertex2f(x+w,y-1)  #fix, givs double px in corner otherwise
  ac.glVertex2f(x+w,y-h)
  ac.glVertex2f(x-1,y-h)  #fix, misses one px in corner otherwise
  ac.glVertex2f(x,y)
  ac.glEnd()   

#draw a line, X,Y cordinate is referenced to left bottom
#end point is inside of same size glQuad
def drawLine(c,x,y,w,h):
  ac.glColor4f(*c)
  #ac.console(">>: l{},{},{},{}".format(x,y,w,h))
  ac.glBegin(0)
  ac.glVertex2f(x,y)
  ac.glVertex2f(x+w,y-h)
  ac.glEnd()   

#create interpolated list and return the value based on x reference
#if x is outside reference range, return the interpolated equalivate if less, otherwise max (gives other errors otherwise)
class Interpolate(object):
    def __init__(self, x_list, y_list):
        if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
          ptLog("proTyres: ERROR Interpolate x_list must be in strictly ascending order!",True)
        x_list = self.x_list = [float(i) for i in x_list]
        y_list = self.y_list = [float(i) for i in y_list]
        intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
        self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]

    def __getitem__(self, x):
        if x < self.x_list[-1]:
          i = bisect_left(self.x_list, x) - 1
          return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
        else:
          return self.y_list[-1]

#create 4 interpolated lists with same base and return the values based on x reference
#if x is outside reference range, return values within range
class InterpolateRGBA(object):
    def __init__(self, x_list, r_list, g_list, b_list, a_list):
        if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
          ptLog("proTyres: ERROR InterpolateRGBA x_list must be in strictly ascending order!",True)
        x_list = self.x_list = [float(i) for i in x_list]
        #interpolate R
        r_list = self.r_list = [float(i) for i in r_list]
        intervals = zip(x_list, x_list[1:], r_list, r_list[1:])
        self.slopesR = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
        #interpolate G
        g_list = self.g_list = [float(i) for i in g_list]
        intervals = zip(x_list, x_list[1:], g_list, g_list[1:])
        self.slopesG = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
        #interpolate B
        b_list = self.b_list = [float(i) for i in b_list]
        intervals = zip(x_list, x_list[1:], b_list, b_list[1:])
        self.slopesB = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
        #interpolate A
        a_list = self.a_list = [float(i) for i in a_list]
        intervals = zip(x_list, x_list[1:], a_list, a_list[1:])
        self.slopesA = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]

    def __getitem__(self, x):
      #does not interpolate at the ends
      if x <= self.x_list[0]:
        return [self.r_list[0],
                self.g_list[0],
                self.b_list[0],
                self.a_list[0]]
      elif x >= self.x_list[-1]:
        return [self.r_list[-1],
                self.g_list[-1],
                self.b_list[-1],
                self.a_list[-1]]
      else:
        i = bisect_left(self.x_list, x) - 1
        return [self.r_list[i] + self.slopesR[i] * (x - self.x_list[i]),
                self.g_list[i] + self.slopesG[i] * (x - self.x_list[i]),
                self.b_list[i] + self.slopesB[i] * (x - self.x_list[i]),
                self.a_list[i] + self.slopesA[i] * (x - self.x_list[i])]
