
"""
Helper lib for GeeXLab demos.
"""

import random 
import gh_utils
import gh_texture
import gh_renderer
import gh_render_target
import gh_camera
import gh_imgui


class gxl_utils:
  
  def __init__(self):
    self._version_major = 0
    self._version_minor = 1
    self._version_patch = 1


  def load_texture_rgba_u8(self, filename):
    PF_U8_RGB = 1
    PF_U8_RGBA = 3
    pixel_format = PF_U8_RGBA
    gen_mipmaps = 1
    compressed_texture = ""
    free_cpu_memory = 1
    t = gh_texture.create_from_file_v6(filename, pixel_format, gen_mipmaps, compressed_texture)
    return t

  def load_texture_rgba_u8_zip(self, zip_filename, filename):
      PF_U8_RGB = 1
      PF_U8_RGBA = 3
      pixel_format = PF_U8_RGBA
      upload_to_gpu = 1
      gen_mipmaps = 1
      compressed_texture = ""
      free_cpu_memory = 1
      t = gh_texture.create_from_zip_file(zip_filename, filename, upload_to_gpu, PF_U8_RGBA, 0, gen_mipmaps, compressed_format)
      return t



  def load_texture_rgba_f32(self, filename):
    PF_F32_RGBA = 6
    pixel_format = PF_F32_RGBA
    gen_mipmaps = 1
    compressed_texture = 0
    free_cpu_memory = 1
    t = gh_texture.create_from_file_v6(filename, pixel_format, gen_mipmaps, compressed_texture)
    return t

  def load_texture_rgba_f32_zip(self, zip_filename, filename):
    PF_F32_RGBA = 6
    pixel_format = PF_F32_RGBA
    upload_to_gpu = 1
    mipmap = 1
    compressed_format = "" 
    t = gh_texture.create_from_zip_file(zip_filename, filename, upload_to_gpu, PF_U8_RGBA, 0, mipmap, compressed_format)
    return t



  def set_sampler_params(self, tex, filtering, addressing, anisotropy):
    SAMPLER_FILTERING_NEAREST = 1
    SAMPLER_FILTERING_LINEAR = 2
    SAMPLER_FILTERING_TRILINEAR = 3
    SAMPLER_ADDRESSING_WRAP = 1
    SAMPLER_ADDRESSING_CLAMP_TO_EDGE = 2
    SAMPLER_ADDRESSING_MIRROR = 3
    SAMPLER_ADDRESSING_CLAMP_TO_BORDER = 4

    filter_ = SAMPLER_FILTERING_LINEAR
    if (filtering == "none"):
      filter_ = SAMPLER_FILTERING_NEAREST
    elif (filtering == "linear"):
      filter_ = SAMPLER_FILTERING_LINEAR
    elif (filtering == "trilinear"):
      filter_ = SAMPLER_FILTERING_TRILINEAR

    addr = SAMPLER_ADDRESSING_WRAP
    if (addressing == "wrap"):
      addr = SAMPLER_ADDRESSING_WRAP
    elif (addressing == "clamp2edge"):
      addr = SAMPLER_ADDRESSING_CLAMP_TO_EDGE
    elif (addressing == "clamp2border"):
      addr = SAMPLER_ADDRESSING_CLAMP_TO_BORDER
    elif (addressing == "mirror"):
      addr = SAMPLER_ADDRESSING_MIRROR

    gh_texture.bind(tex, 0)
    gh_texture.set_sampler_params(tex, filter_, addr, anisotropy)
    gh_texture.bind(0, 0)



  def random_init(self, seed):
    random.seed = seed

  def random(self, a, b):
    return random.uniform(a, b)

  def float2int(self, a):
    return int(round(a))


  def to_radians(self, angle_degrees):
    return angle_degrees * 3.14159265 / 180.0



  def create_depth_render_target(self, width, height):
    linear_filtering = 1
    rt = gh_render_target.create_depth(width, height, linear_filtering)
    return rt

  def create_color_render_target_rgba_u8(self, width, height, msaa):
    rt = 0
    PF_U8_RGBA = 3
    num_targets = 1
    if (msaa > 0):
      rt = gh_render_target.create_rb(width, height, PF_U8_RGBA, msaa)
    else:
      rt = gh_render_target.create_ex(width, height, PF_U8_RGBA, num_targets, 0)
    return rt

  def create_color_render_target_rgba_f32(self, width, height, msaa):
    rt = 0
    PF_F32_RGBA = 6
    num_targets = 1
    if (msaa > 0):
      rt = gh_render_target.create_rb(width, height, PF_F32_RGBA, msaa)
    else:
      rt = gh_render_target.create_ex(width, height, PF_F32_RGBA, num_targets, 0)
    return rt

  def create_color_render_target_rgba_f16(self, width, height, msaa):
    rt = 0
    PF_F16_RGBA = 9
    num_targets = 1
    if (msaa > 0):
      rt = gh_render_target.create_rb(width, height, PF_F16_RGBA, msaa)
    else:
      rt = gh_render_target.create_ex(width, height, PF_F16_RGBA, num_targets, 0)
    return rt

  def enable_msaa(self, state):
    gh_renderer.rasterizer_set_msaa_state(state)
    gh_renderer.rasterizer_apply_states()





  def create_camera_3d(self, fov, width, height, znear, zfar):
    aspect = 1.333
    if (height > 0):
      aspect = width / height
    
    camera = gh_camera.create_persp(fov, aspect, znear, zfar)
    gh_camera.set_viewport(camera, 0, 0, width, height)
    gh_camera.setpos(camera, 0, 10, 50)
    gh_camera.setlookat(camera, 0, 0, 0, 1)
    gh_camera.setupvec(camera, 0, 1, 0, 0)
    return camera

  def resize_camera_3d(self, camera, fov, width, height, znear, zfar):
    aspect = 1.333
    if (height > 0):
      aspect = width / height
    gh_camera.update_persp(camera, fov, aspect, znear, zfar)
    gh_camera.set_viewport(camera, 0, 0, width, height)

  def create_camera_2d(self, width, height, znear, zfar):
    camera = gh_camera.create_ortho(-width/2, width/2, -height/2, height/2, znear, zfar)
    gh_camera.set_viewport(camera, 0, 0, width, height)
    gh_camera.set_position(camera, 0, 0, 4)
    return camera

  def resize_camera_2d(self, camera, width, height, znear, zfar):
    gh_camera.update_ortho(camera, -width/2, width/2, -height/2, height/2, znear, zfar)
    gh_camera.set_viewport(camera, 0, 0, width, height)


  def cull_back_faces(self):
    POLYGON_FACE_NONE = 0
    POLYGON_FACE_BACK = 1
    POLYGON_FACE_FRONT = 2
    POLYGON_FACE_BACK_FRONT = 3
    gh_renderer.rasterizer_set_cull_state(1)
    gh_renderer.rasterizer_set_cull_face(POLYGON_FACE_BACK)
    gh_renderer.rasterizer_apply_states()

  def cull_front_faces(self):
    POLYGON_FACE_NONE = 0
    POLYGON_FACE_BACK = 1
    POLYGON_FACE_FRONT = 2
    POLYGON_FACE_BACK_FRONT = 3
    gh_renderer.rasterizer_set_cull_state(1)
    gh_renderer.rasterizer_set_cull_face(POLYGON_FACE_FRONT)
    gh_renderer.rasterizer_apply_states()

  def cull_none_faces(self):
    POLYGON_FACE_NONE = 0
    POLYGON_FACE_BACK = 1
    POLYGON_FACE_FRONT = 2
    POLYGON_FACE_BACK_FRONT = 3
    gh_renderer.rasterizer_set_cull_state(0)
    gh_renderer.rasterizer_set_cull_face(POLYGON_FACE_NONE)
    gh_renderer.rasterizer_apply_states()


  def print_quaternion(self, q):
    gh_utils.trace("<%f ; %f ; %f ; %f>" % (q.x, q.y, q.z, q.w))

  def imgui_print_quaternion(self, q):
    gh_imgui.text("<%f ; %f ; %f ; %f>" % (q.x, q.y, q.z, q.w))



  # HLS to RGB and RGB to HSL
  # via: https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
  # https://en.wikipedia.org/wiki/HSL_and_HSV

  def _hue2rgb(self, p, q, t):
    if(t < 0):
      t = t + 1
    if(t > 1):
      t = t - 1
    if(t < 1/6):
      return p + (q - p) * 6.0 * t
    if(t < 1/2):
      return q
    if(t < 2/3):
      return p + (q - p) * (2.0/3.0 - t) * 6.0
    return p

  def hsl_to_rgb(self, h, s, l):
    r, g, b

    if (s == 0.0):
      r = l # achromatic
      g = l # achromatic
      b = l # achromatic
    else:
      q = 0.0

      if (l < 0.5 ):
        q = l * (1.0 + s) 
      else:
        q = l + s - l * s

      p = 2.0 * l - q
      r = gxl._hue2rgb(p, q, h + 1.0/3.0)
      g = gxl._hue2rgb(p, q, h)
      b = gxl._hue2rgb(p, q, h - 1.0/3.0)

    return r, g, b

  def rgb_to_hsl(self, r, g, b):
    max = math.max(r, g, b)
    min = math.min(r, g, b)
    h, s, l = (max + min) / 2.0

    if(max == min):
      h = 0
      s = 0 # achromatic
    else:
      d = max - min
      s = 0
      if (l > 0.5):
        s = d / (2 - max - min)
      else:
        s = d / (max + min)
      
      if (max == r):
        x = 0.0
        if (g < b):
          x = 6.0
        h = (g - b) / d + x

      elif (max == g):
        h = (b - r) / d + 2
      
      elif (max == b):
        h = (r - g) / d + 4

      h = h / 6.0

    return h, s, l
