// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "pch.h"
#include "ImSequencer.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <cstdlib>

#include "engine/EditorNew.h"
#include "engine/Engine.h"
#include "engine/IO.h"


namespace ImSequencer
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
   static ImVec2 operator+(const ImVec2& a, const ImVec2& b) {
      return ImVec2(a.x + b.x, a.y + b.y);
   }
#endif
   static bool SequencerAddDelButton(ImDrawList* draw_list, ImVec2 pos, bool add = true)
   {
      ImGuiIO& io = ImGui::GetIO();
      ImRect btnRect(pos, ImVec2(pos.x + 16, pos.y + 16));
      bool overBtn = btnRect.Contains(io.MousePos);
      bool containedClick = overBtn && btnRect.Contains(io.MouseClickedPos[0]);
      bool clickedBtn = containedClick && io.MouseReleased[0];
      int btnColor = overBtn ? 0xAAEAFFAA : 0x77A3B2AA;
      if (containedClick && io.MouseDownDuration[0] > 0)
         btnRect.Expand(2.0f);

      float midy = pos.y + 16 / 2 - 0.5f;
      float midx = pos.x + 16 / 2 - 0.5f;
      draw_list->AddRect(btnRect.Min, btnRect.Max, btnColor, 4);
      draw_list->AddLine(ImVec2(btnRect.Min.x + 3, midy), ImVec2(btnRect.Max.x - 3, midy), btnColor, 2);
      if (add)
         draw_list->AddLine(ImVec2(midx, btnRect.Min.y + 3), ImVec2(midx, btnRect.Max.y - 3), btnColor, 2);
      return clickedBtn;
   }

   bool DoSequencer(
      SequenceInterface* sequence,
      int* currentFrame,
      bool* expanded,
      int* selectedEntry,
      int* firstFrame, 
      int sequenceOptions
   ) {
      ImGui::Checkbox("Pause time", &WEngine->editor->timePaused);
      ImGui::SameLine();
      ImGui::Text("Time (beatsynced): %.2f", WEngine->editor->music_time);
			
      bool selected = selectedEntry && (*selectedEntry >= 0);
      bool ret = false;
      ImGuiIO& io = ImGui::GetIO();
      int cx = (int)(io.MousePos.x);
      int cy = (int)(io.MousePos.y);
      static float framePixelWidth = 10.f;
      static float framePixelWidthTarget = 10.f;
      int legendWidth = 110;

      static int movingEntry = -1;
      static int movingPos = -1;
      static int movingPart = -1;
      int delEntry = -1;
      int dupEntry = -1;
      int ItemHeight = 20;

      bool popupOpened = false;
      int sequenceCount = sequence->GetLanesCount();
      if (sequenceCount == 0)
         return false;
      ImGui::BeginGroup();

      ImDrawList* draw_list = ImGui::GetWindowDrawList();
      ImVec2 canvas_pos = ImGui::GetCursorScreenPos();            // ImDrawList API uses screen coordinates!
      ImVec2 canvas_size = ImGui::GetContentRegionAvail();        // Resize canvas to what's available
      int firstFrameUsed = firstFrame ? *firstFrame : 0;


      int controlHeight = sequenceCount * ItemHeight;
      for (int i = 0; i < sequenceCount; i++)
         controlHeight += int(sequence->GetCustomHeight(i));
      int frameCount = ImMax(sequence->GetFrameMax() - sequence->GetFrameMin(), 1);

      static bool MovingScrollBar = false;
      static bool MovingCurrentFrame = false;
      struct CustomDraw {
         int index;
         ImRect customRect;
         ImRect legendRect;
         ImRect clippingRect;
         ImRect legendClippingRect;
      };
      ImVector<CustomDraw> customDraws;
      ImVector<CustomDraw> compactCustomDraws;
      // zoom in/out
      const int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth);
      const float barWidthRatio = ImMin(visibleFrameCount / (float)frameCount, 1.f);
      const float barWidthInPixels = barWidthRatio * (canvas_size.x - legendWidth);

      ImRect regionRect(canvas_pos, canvas_pos + canvas_size);

      static bool panningView = false;
      static ImVec2 panningViewSource;
      static int panningViewFrame;


      // ?? wth is this
      // if (ImGui::IsWindowFocused() && io.KeyAlt && io.MouseDown[2]) {
      //    if (!panningView) {
      //       panningViewSource = io.MousePos;
      //       panningView = true;
      //       panningViewFrame = *firstFrame;
      //    }
      //    *firstFrame = panningViewFrame - int((io.MousePos.x - panningViewSource.x) / framePixelWidth);
      //    *firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), sequence->GetFrameMax() - visibleFrameCount);
      // }
      // if (panningView && !io.MouseDown[2]) {
      //    panningView = false;
      // }
      framePixelWidthTarget = ImClamp(framePixelWidthTarget, 0.1f, 50.f);

      // framePixelWidth = ImLerp(framePixelWidth, framePixelWidthTarget, 0.33f);
      framePixelWidth = ImLerp(framePixelWidth, framePixelWidthTarget, 0.33f);

      frameCount = sequence->GetFrameMax() - sequence->GetFrameMin();
      if (visibleFrameCount >= frameCount && firstFrame)
         *firstFrame = sequence->GetFrameMin();

      // --STUFF ON LEFT -- //
      if (expanded && !*expanded) {
         ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x - canvas_pos.x, (float)ItemHeight));
         draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
         char tmps[512];
         ImFormatString(tmps, IM_ARRAYSIZE(tmps), sequence->GetCollapseFmt(), frameCount, sequenceCount);
         draw_list->AddText(ImVec2(canvas_pos.x + 26, canvas_pos.y + 2), 0xFFFFFFFF, tmps);
      } else {
         bool hasScrollBar(true);
         /*
         int framesPixelWidth = int(frameCount * framePixelWidth);
         if ((framesPixelWidth + legendWidth) >= canvas_size.x)
         {
             hasScrollBar = true;
         }
         */
         ImVec2 childFramePos;
         ImVec2 contentMin;
         ImVec2 contentMax;
         ImRect contentRect;
         float contentHeight;
         ImVec2 childFrameSize;
         
         ImVec2 headerSize(canvas_size.x, (float)ItemHeight);
         ImVec2 scrollBarSize(canvas_size.x, 14.f);
         {
               
            // test scroll area
            ImGui::InvisibleButton("topBar", headerSize);
            draw_list->AddRectFilled(canvas_pos, canvas_pos + headerSize, 0xFFFF0000, 0);
            childFramePos = ImGui::GetCursorScreenPos();
            childFrameSize = ImVec2(canvas_size.x, canvas_size.y - 8.f - headerSize.y - (hasScrollBar ? scrollBarSize.y : 0));
            ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
            ImGui::BeginChildFrame(889, childFrameSize);
            sequence->focused = ImGui::IsWindowFocused();
            ImGui::InvisibleButton("contentBar", ImVec2(canvas_size.x, float(controlHeight)));
            contentMin = ImGui::GetItemRectMin();
            contentMax = ImGui::GetItemRectMax();
            contentRect = ImRect(contentMin, contentMax);
            contentHeight = contentMax.y - contentMin.y;

            // full background
            draw_list->AddRectFilled(canvas_pos, canvas_pos + canvas_size, 0xFF242424, 0);

            // current frame top
            ImRect topRect(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight));

            if (!MovingCurrentFrame && !MovingScrollBar && movingEntry == -1 && sequenceOptions & SEQUENCER_CHANGE_FRAME && currentFrame && *currentFrame >= 0 && topRect.Contains(io.MousePos) && io.MouseDown[0]) {
               MovingCurrentFrame = true;
            }
            if (MovingCurrentFrame) {
               if (frameCount) {
                  *currentFrame = (int)((io.MousePos.x - topRect.Min.x) / framePixelWidth) + firstFrameUsed;
                  if (*currentFrame < sequence->GetFrameMin())
                     *currentFrame = sequence->GetFrameMin();
                  if (*currentFrame >= sequence->GetFrameMax())
                     *currentFrame = sequence->GetFrameMax();
               }
               if (!io.MouseDown[0]) {
                  MovingCurrentFrame = false;
               }
            }
            
         }

         ImRect header_rect = ImRect(
            ImVec2(canvas_pos.x, canvas_pos.y),
            ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight)
         );
         
         int frameStep = 5;
         int modFrameCount = 10;
         int halfModFrameCount;
         
         size_t customHeight = 0;
         
         auto do_top = [&](){
            // ---------------- TIME MOUSE SCROBBLING ----------------- //
            if(header_rect.Contains(io.MousePos) && io.MouseDown[0]) {
               float xpos = 0;
               xpos -= header_rect.GetBL().x;
               xpos -= legendWidth;
               xpos += float(io.MousePos.x);
               xpos += firstFrameUsed*framePixelWidth;
               xpos /= framePixelWidth + 0.*float(firstFrameUsed * framePixelWidth);
               WEngine->time = xpos / 10.0f / WEngine->editor->timeline.bpm * 60.0f;
               // wlog_info("{}",xpos);
               // wlog_info("potat");
            }
            //header bg
            draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
            if (sequenceOptions & SEQUENCER_ADD)
            {
               // if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight, canvas_pos.y + 2), true))
               //    ImGui::OpenPopup("addEntry");

               // if (ImGui::BeginPopup("addEntry"))
               // {
               //    for (int i = 0; i < sequence->GetItemTypeCount(); i++)
               //       if (ImGui::Selectable(sequence->GetItemTypeName(i)))
               //       {
               //          sequence->AddSequence(i);
               //          *selectedEntry = sequence->GetSequencesCount() - 1;
               //       }
               //
               //    ImGui::EndPopup();
               //    popupOpened = true;
               // }
            }

            // ---------------- header frame number and lines ----------------- //
            // ---------------- header frame number and lines ----------------- //
            // ---------------- header frame number and lines ----------------- //
            // ---------------- header frame number and lines ----------------- //
            // ---------------- header frame number and lines ----------------- //

            auto drawLine = [&](int i, int regionHeight) {
               bool baseIndex = ((i % modFrameCount) == 0) || (i == sequence->GetFrameMax() || i == sequence->GetFrameMin());
               bool halfIndex = (i % halfModFrameCount) == 0;
               int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
               int tiretStart = baseIndex ? 4 : (halfIndex ? 10 : 14);
               int tiretEnd = baseIndex ? regionHeight : ItemHeight;

               if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth)) {
                  draw_list->AddLine(
                     ImVec2((float)px, canvas_pos.y + (float)tiretStart), 
                     ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 
                     0xFF606060, 
                     1
                  );

                  draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)ItemHeight), ImVec2((float)px, canvas_pos.y + (float)regionHeight - 1), 0x30606060, 1);
               }

               if (baseIndex && px > (canvas_pos.x + legendWidth)) {
                  char tmps[512];
                  ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", i/10);
                  draw_list->AddText(ImVec2((float)px + 3.f, canvas_pos.y), 0xFFBBBBBB, tmps);
               }

            };

            
            // ------------ TOP TIME LINES --------------- //
            for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax()*2; i += frameStep) {
               drawLine(i, ItemHeight);
            }
            drawLine(sequence->GetFrameMin(), ItemHeight);
            drawLine(sequence->GetFrameMax(), ItemHeight);
            /*
                     draw_list->AddLine(canvas_pos, ImVec2(canvas_pos.x, canvas_pos.y + controlHeight), 0xFF000000, 1);
                     draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight), ImVec2(canvas_size.x, canvas_pos.y + ItemHeight), 0xFF000000, 1);
                     */
                     // clip content
         };

         auto do_left_lane_title_names = [&](){
            // ------------ LEFT LANE TITLE NAMES --------------- //
            // draw item names in the legend rect on the left
            for (int i = 0; i < sequenceCount; i++) {
               int type;
               sequence->GetSequence(i, NULL, NULL, &type, NULL);
               ImVec2 tpos(contentMin.x + 3, contentMin.y + i * ItemHeight + 2 + customHeight);
               draw_list->AddText(tpos, 0xFFFFFFFF, sequence->GetLaneLabel(i));

               if (sequenceOptions & SEQUENCER_DEL)
               {
               
                  // if(sequence->GetSequence())
                  // ---------------- LEFT LANE TOGGLE ---------------- //
                  if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight + 2 - 10, tpos.y + 2), false)) {
                     // WEngine->editor->timeline.lanes[i]
                     WEngine->editor->timeline.DoubleClick(i);
                  }
                  // delEntry = i;

                  // if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight - ItemHeight + 2 - 10, tpos.y + 2), true))
                  //    dupEntry = i;
               }
               customHeight += sequence->GetCustomHeight(i);
            }
         };
         
         auto do_lane_seq_background = [&](){
            auto drawLineContent = [&](
               int i, 
               int /*regionHeight*/, 
               int width = 1,
               int colour = 0x30606060
            ) {
               int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
               int tiretStart = int(contentMin.y);
               int tiretEnd = int(contentMax.y);

               if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth)) {
                  //draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1);

                  draw_list->AddLine(
                  ImVec2(float(px), float(tiretStart)), 
                  ImVec2(float(px), float(tiretEnd)), 
                  colour, 
                  // 1 + (i%2)*10
                  width
                  );
               }
            };
            // slots background
            customHeight = 0;
            for (int i = 0; i < sequenceCount; i++) {
               unsigned int col = (i & 1) ? 0x333A3636 : 0x33413D3D;

               size_t localCustomHeight = sequence->GetCustomHeight(i);
               ImVec2 pos = ImVec2(contentMin.x + legendWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
               ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1 + localCustomHeight);
               if (
                  !popupOpened && 
                  cy >= pos.y && 
                  cy < pos.y + (ItemHeight + localCustomHeight) && 
                  movingEntry == -1 && 
                  cx>contentMin.x && 
                  cx < contentMin.x + canvas_size.x
               ) {
                  col += 0x10201008;
                  pos.x -= legendWidth;
               }
               draw_list->AddRectFilled(pos, sz, col, 0);
               customHeight += localCustomHeight;
            }

            draw_list->PushClipRect(childFramePos + ImVec2(float(legendWidth), 0.f), childFramePos + childFrameSize, true);

            // ---------- VERTICAL LINES ---------- //
            for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax()*2; i += frameStep) {
               if(i%64 == 0) {
                  drawLineContent(i, int(contentHeight), 2, 0xF0404080);
               } else if(i%16 == 0) {
                  drawLineContent(i, int(contentHeight), 2, 0x80D0D0D0);
               } else if(i%8 == 0){
                  drawLineContent(i, int(contentHeight), 2, 0x30D0D0D0);
               } else {
                  drawLineContent(i, int(contentHeight), 1);
               }
            }
            drawLineContent(sequence->GetFrameMin(), int(contentHeight));
            drawLineContent(sequence->GetFrameMax(), int(contentHeight));

            // selection
            if (selected) {
               customHeight = 0;
               for (int i = 0; i < *selectedEntry; i++)
                  customHeight += sequence->GetCustomHeight(i);
               draw_list->AddRectFilled(ImVec2(contentMin.x, contentMin.y + ItemHeight * *selectedEntry + customHeight), ImVec2(contentMin.x + canvas_size.x, contentMin.y + ItemHeight * (*selectedEntry + 1) + customHeight), 0x801080FF, 1.f);
            }
            
         };
         
         auto queue_sequence_drwaings = [&](){
            for (int sequence_idx = 0; sequence_idx < sequenceCount; sequence_idx++) {
               int* start, * end;
               unsigned int color;
               sequence->GetSequence(sequence_idx, &start, &end, NULL, &color);
               size_t localCustomHeight = sequence->GetCustomHeight(sequence_idx);

               ImVec2 pos = ImVec2(contentMin.x + legendWidth - firstFrameUsed * framePixelWidth, contentMin.y + ItemHeight * sequence_idx + 1 + customHeight);
               ImVec2 slotP1(pos.x + *start * framePixelWidth, pos.y + 2);
               ImVec2 slotP2(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2);
               ImVec2 slotP3(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2 + localCustomHeight);
               unsigned int slotColor = color | 0xFF000000;
               unsigned int slotColorHalf = (color & 0xFFFFFF) | 0x40000000;

               if (slotP1.x <= (canvas_size.x + contentMin.x) && slotP2.x >= (contentMin.x + legendWidth))
               {
                  // draw_list->AddRectFilled(slotP1, slotP3, slotColorHalf, 2);
                  // draw_list->AddRectFilled(slotP1, slotP2, slotColor, 2);
               }
               // if (ImRect(slotP1, slotP2).Contains(io.MousePos) && io.MouseDoubleClicked[0]) {
               //    sequence->DoubleClick(sequence_idx);
               // }
               // Ensure grabbable handles
               const float max_handle_width = slotP2.x - slotP1.x / 3.0f;
               const float min_handle_width = ImMin(10.0f, max_handle_width);
               const float handle_width = ImClamp(framePixelWidth / 2.0f, min_handle_width, max_handle_width);
               ImRect rects[3] = { ImRect(slotP1, ImVec2(slotP1.x + handle_width, slotP2.y))
                   , ImRect(ImVec2(slotP2.x - handle_width, slotP1.y), slotP2)
                   , ImRect(slotP1, slotP2) };

               const unsigned int quadColor[] = { 0xFFFFFFFF, 0xFFFFFFFF, slotColor + (selected ? 0 : 0x202020) };
               if (movingEntry == -1 && (sequenceOptions & SEQUENCER_EDIT_STARTEND)) {
                  // TODOFOCUS && backgroundRect.Contains(io.MousePos))
                  // for (int j = 2; j >= 0; j--) {
                  //    ImRect& rc = rects[j];
                  //    if (!rc.Contains(io.MousePos))
                  //       continue;
                  //    // draw_list->AddRectFilled(rc.Min, rc.Max, quadColor[j], 2);
                  // }

                  // for (int j = 0; j < 3; j++) {
                  //    ImRect& rc = rects[j];
                  //    if (!rc.Contains(io.MousePos))
                  //       continue;
                  //    if (!ImRect(childFramePos, childFramePos + childFrameSize).Contains(io.MousePos))
                  //       continue;
                  //    if (ImGui::IsMouseClicked(0) && !MovingScrollBar && !MovingCurrentFrame) {
                  //       movingEntry = sequence_idx;
                  //       movingPos = cx;
                  //       movingPart = j + 1;
                  //       sequence->BeginEdit(movingEntry);
                  //       break;
                  //    }
                  // }
               }

               // custom draw
               // ----------------- QUEUE LANE DRAWS ---------------------- //
               
                  ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * sequence_idx + customHeight);
                  ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(0.f)),
                     rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(ItemHeight)));
                  ImRect clippingRect(rp + ImVec2(float(legendWidth), float(0.f)), rp + ImVec2(canvas_size.x, float(ItemHeight)));

                  if(clippingRect.Contains(io.MousePos) && io.MouseClicked[0]) {
                     WEngine->editor->timeline.DoubleClick(sequence_idx);
                  }
               if (localCustomHeight > 0) {
                  ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * sequence_idx + 1 + customHeight);
                  ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(ItemHeight)),
                     rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(localCustomHeight + ItemHeight)));
                  ImRect clippingRect(rp + ImVec2(float(legendWidth), float(ItemHeight)), rp + ImVec2(canvas_size.x, float(localCustomHeight + ItemHeight)));

                  ImRect legendRect(rp + ImVec2(0.f, float(ItemHeight)), rp + ImVec2(float(legendWidth), float(localCustomHeight)));
                  ImRect legendClippingRect(canvas_pos + ImVec2(0.f, float(ItemHeight)), canvas_pos + ImVec2(float(legendWidth), float(localCustomHeight + ItemHeight)));
                  customDraws.push_back({ sequence_idx, customRect, legendRect, clippingRect, legendClippingRect });
               } else {
                  // compactCustomDraws.push_back({ sequence_idx, customRect, ImRect(), clippingRect, ImRect() });
               }
               customHeight += localCustomHeight;
            }
         };
         
         auto do_bottom_scrollbar = [&](){
            if (hasScrollBar) {
               ImGui::InvisibleButton("scrollBar", scrollBarSize);
               ImVec2 scrollBarMin = ImGui::GetItemRectMin();
               ImVec2 scrollBarMax = ImGui::GetItemRectMax();

               // ratio = number of frames visible in control / number to total frames

               float startFrameOffset = ((float)(firstFrameUsed - sequence->GetFrameMin()) / (float)frameCount) * (canvas_size.x - legendWidth);
               ImVec2 scrollBarA(scrollBarMin.x + legendWidth, scrollBarMin.y - 2);
               ImVec2 scrollBarB(scrollBarMin.x + canvas_size.x, scrollBarMax.y - 1);
               draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0);

               ImRect scrollBarRect(scrollBarA, scrollBarB);
               bool inScrollBar = scrollBarRect.Contains(io.MousePos);

               draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, 8);


               ImVec2 scrollBarC(scrollBarMin.x + legendWidth + startFrameOffset, scrollBarMin.y);
               ImVec2 scrollBarD(scrollBarMin.x + legendWidth + barWidthInPixels + startFrameOffset, scrollBarMax.y - 2);
               draw_list->AddRectFilled(scrollBarC, scrollBarD, (inScrollBar || MovingScrollBar) ? 0xFF606060 : 0xFF505050, 6);

               ImRect barHandleLeft(scrollBarC, ImVec2(scrollBarC.x + 14, scrollBarD.y));
               ImRect barHandleRight(ImVec2(scrollBarD.x - 14, scrollBarC.y), scrollBarD);

               bool onLeft = barHandleLeft.Contains(io.MousePos);
               bool onRight = barHandleRight.Contains(io.MousePos);

               static bool sizingRBar = false;
               static bool sizingLBar = false;

               draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, (onLeft || sizingLBar) ? 0xFFAAAAAA : 0xFF666666, 6);
               draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, (onRight || sizingRBar) ? 0xFFAAAAAA : 0xFF666666, 6);

               
               // ImVec2 pos = ImVec2(contentMin.x + legendWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
               // ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1 + localCustomHeight);
               
               ImRect scrollBarThumb(scrollBarC, scrollBarD);
               static const float MinBarWidth = 6.f;

               
               int bar_max_range = ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin()) + 1000;
               // ----- TRANSLATE MMB ----- //
               if(glm::abs(io.MouseWheel) > 0.001 && WEngine->io->get_key(Key::LCtrl).down) {
                  // float barNewWidth = ImMax(barWidthInPixels + io.MouseWheel*10, MinBarWidth);
                  // float mouse_perc = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
                  float mouse_percentage = float(io.MousePos.x - canvas_pos.x - legendWidth)/float(canvas_size.x - legendWidth);
                  
                  // wlog_info("{}", mouse_percentage);
                  float bar_trans_amt = io.MouseWheel*70;
                  
                  // float barNewWidth = ImMax(barWidthInPixels - bar_trans_amt*(mouse_percentage), MinBarWidth);
                  // float barRatio = barWidthInPixels;

                  
                  float desired_zoom = 1.0;
                  desired_zoom = barWidthInPixels/(canvas_size.x - legendWidth);
                  float z = glm::pow(2.0f, glm::log2(desired_zoom));

                  bar_trans_amt *= z;
                  {
                     float barNewWidth = ImMax(barWidthInPixels - bar_trans_amt*(mouse_percentage), MinBarWidth);
                     float barRatio = barNewWidth / barWidthInPixels;
                     float previousFramePixelWidthTarget = framePixelWidthTarget;
                     framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
                     int newVisibleFrameCount = int(visibleFrameCount / barRatio);
                     int newFirstFrame = *firstFrame + newVisibleFrameCount - visibleFrameCount;
                     newFirstFrame = ImClamp(newFirstFrame, sequence->GetFrameMin() - 20, bar_max_range);
                     if (newFirstFrame == *firstFrame) {
                        framePixelWidth = framePixelWidthTarget = previousFramePixelWidthTarget;
                     } else {
                        *firstFrame = newFirstFrame;
                     }
                  }
                  {
                     float barNewWidth = ImMax(barWidthInPixels - bar_trans_amt*(1-mouse_percentage), MinBarWidth);
                     float barRatio = barNewWidth / barWidthInPixels;
                     framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
                     int newVisibleFrameCount = int((canvas_size.x - legendWidth) / framePixelWidthTarget);
                     int lastFrame = *firstFrame + newVisibleFrameCount;
                     if (lastFrame > sequence->GetFrameMax()) {
                        framePixelWidthTarget = framePixelWidth = (canvas_size.x - legendWidth) / float(sequence->GetFrameMax() - *firstFrame);
                     }
                  }
                  
               }
               // ----- RESIZE BAR ----- //
               if (sizingRBar) {
                  if (!io.MouseDown[0]) {
                     sizingRBar = false;
                  } else {
                     float barNewWidth = ImMax(barWidthInPixels + io.MouseDelta.x, MinBarWidth);
                     float barRatio = barNewWidth / barWidthInPixels;
                     framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
                     int newVisibleFrameCount = int((canvas_size.x - legendWidth) / framePixelWidthTarget);
                     int lastFrame = *firstFrame + newVisibleFrameCount;
                     if (lastFrame > sequence->GetFrameMax()) {
                        framePixelWidthTarget = framePixelWidth = (canvas_size.x - legendWidth) / float(sequence->GetFrameMax() - *firstFrame);
                     }
                  }
               } else if (sizingLBar) {
                  if (!io.MouseDown[0]) {
                     sizingLBar = false;
                  } else {
                     if (fabsf(io.MouseDelta.x) > FLT_EPSILON) {
                        float barNewWidth = ImMax(barWidthInPixels - io.MouseDelta.x, MinBarWidth);
                        float barRatio = barNewWidth / barWidthInPixels;
                        float previousFramePixelWidthTarget = framePixelWidthTarget;
                        framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
                        int newVisibleFrameCount = int(visibleFrameCount / barRatio);
                        int newFirstFrame = *firstFrame + newVisibleFrameCount - visibleFrameCount;
                        newFirstFrame = ImClamp(newFirstFrame, sequence->GetFrameMin() - 20, bar_max_range);
                        if (newFirstFrame == *firstFrame) {
                           framePixelWidth = framePixelWidthTarget = previousFramePixelWidthTarget;
                        } else {
                           *firstFrame = newFirstFrame;
                        }
                     }
                  }
               } else {
                  if (MovingScrollBar) {
                     if (!io.MouseDown[0] && !io.MouseDown[2]) {
                        MovingScrollBar = false;
                     } else {
                        float framesPerPixelInBar = barWidthInPixels / (float)visibleFrameCount;
                        if(glm::abs(io.MouseDelta.x) > 0.001 && glm::abs(io.MouseDelta.x) < 100) {
                           int offs = int((io.MouseDelta.x) / framesPerPixelInBar);
                           if(io.MouseDown[2]) {
                              offs = -offs;
                           }
                           *firstFrame += offs;
                        }
                        // *firstFrame = int((io.MousePos.x - panningViewSource.x) / framesPerPixelInBar) - panningViewFrame;
                        *firstFrame = ImClamp(
                           *firstFrame, 
                           sequence->GetFrameMin() - 20,
                           // ImMax(sequence->GetFrameMax() - visibleFrameCount + 100, sequence->GetFrameMin())
                           bar_max_range
                           );
                     }
                  } else {
                     if (
                        (
                           scrollBarThumb.Contains(io.MousePos) &&
                           ImGui::IsMouseClicked(0) &&
                           firstFrame &&
                           !MovingCurrentFrame
                           && movingEntry == -1
                        ) || ImGui::IsMouseClicked(2)
                     ) {
                        MovingScrollBar = true;
                        panningViewSource = io.MousePos;
                        panningViewFrame = -*firstFrame;
                     }
                     if (!sizingRBar && onRight && ImGui::IsMouseClicked(0))
                        sizingRBar = true;
                     if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0))
                        sizingLBar = true;

                  }
               }
            }
         };
         
         
         auto do_cursor = [&](){
            if (true) {
               float time = WEngine->editor->music_time;
               static const float cursorWidth = 2.f;
               float cursorOffset = contentMin.x
                  + legendWidth
                  // + framePixelWidth / 2
                  - cursorWidth * 0.5f
                  - 9.0f
                  - int(firstFrameUsed * framePixelWidth) // SKRUNKLY OFFSET
               ;
               cursorOffset += (time*10) * framePixelWidth;
               cursorOffset += framePixelWidth / 2;
               draw_list->AddLine(ImVec2(cursorOffset, canvas_pos.y), ImVec2(cursorOffset, contentMax.y), 0xFFFFFFFF, cursorWidth);
               char tmps[512];
               ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", *currentFrame);
               draw_list->AddText(ImVec2(cursorOffset + 10, canvas_pos.y + 2), 0xFF2A2AFF, tmps);
            }
         };
         
         auto do_sequenced_draws = [&](){
            // --------- DO SEQUENCED DRAWS --------- //
            for (auto& customDraw : customDraws) {
               ImRect crect = customDraw.clippingRect;
               // crect.Min.x += 100.1;
               // crect.Max.x += 100.1;
               sequence->CustomDraw(customDraw.index, draw_list, customDraw.customRect, customDraw.legendRect, crect, customDraw.legendClippingRect);
            }
         
            for (auto& customDraw : compactCustomDraws)
               sequence->CustomDrawCompact(customDraw.index, draw_list, customDraw.customRect, customDraw.clippingRect);
         };
         
         while ((modFrameCount * framePixelWidth) < 40) {
            modFrameCount *= 2;
            frameStep *= 2;
         };
         halfModFrameCount = modFrameCount / 2;
         
         do_top();

         draw_list->PushClipRect(childFramePos, childFramePos + childFrameSize, true);
         
         do_left_lane_title_names();

         do_lane_seq_background();


         customHeight = 0;
         queue_sequence_drwaings();

         do_cursor();

         draw_list->PopClipRect();
         draw_list->PopClipRect();
         
         do_sequenced_draws();

         ImGui::EndChildFrame();
         ImGui::PopStyleColor();

         do_bottom_scrollbar();
         
         // moving
         if (/*backgroundRect.Contains(io.MousePos) && */movingEntry >= 0 && false)
         {
            #if IMGUI_VERSION_NUM >= 18723
            ImGui::SetNextFrameWantCaptureMouse(true);
            #else
            ImGui::CaptureMouseFromApp();
            #endif
            int diffFrame = int((cx - movingPos) / framePixelWidth);
            if (std::abs(diffFrame) > 0)
            {
               int* start, * end;
               sequence->GetSequence(movingEntry, &start, &end, NULL, NULL);
               if (selectedEntry)
                  *selectedEntry = movingEntry;
               int& l = *start;
               int& r = *end;
               if (movingPart & 1)
                  l += diffFrame;
               if (movingPart & 2)
                  r += diffFrame;
               if (l < 0)
               {
                  if (movingPart & 2)
                     r -= l;
                  l = 0;
               }
               if (movingPart & 1 && l > r)
                  l = r;
               if (movingPart & 2 && r < l)
                  r = l;
               movingPos += int(diffFrame * framePixelWidth);
            }
            if (!io.MouseDown[0]) {
               // single select
               if (!diffFrame && movingPart && selectedEntry) {
                  *selectedEntry = movingEntry;
                  ret = true;
               }

               movingEntry = -1;
               sequence->EndEdit();
            }
         }
         
         // copy paste
         if (sequenceOptions & SEQUENCER_COPYPASTE) {
            // ImRect rectCopy(ImVec2(contentMin.x + 100, canvas_pos.y + 2)
            //    , ImVec2(contentMin.x + 100 + 30, canvas_pos.y + ItemHeight - 2));
            // bool inRectCopy = rectCopy.Contains(io.MousePos);
            // unsigned int copyColor = inRectCopy ? 0xFF1080FF : 0xFF000000;
            // draw_list->AddText(rectCopy.Min, copyColor, "Copy");
            //
            // ImRect rectPaste(ImVec2(contentMin.x + 140, canvas_pos.y + 2)
            //    , ImVec2(contentMin.x + 140 + 30, canvas_pos.y + ItemHeight - 2));
            // bool inRectPaste = rectPaste.Contains(io.MousePos);
            // unsigned int pasteColor = inRectPaste ? 0xFF1080FF : 0xFF000000;
            // draw_list->AddText(rectPaste.Min, pasteColor, "Paste");
            //
            // if (inRectCopy && io.MouseReleased[0])
            // {
            //    sequence->Copy();
            // }
            // if (inRectPaste && io.MouseReleased[0])
            // {
            //    sequence->Paste();
            // }
         }
      }

      ImGui::EndGroup();

      if (regionRect.Contains(io.MousePos))
      {
         bool overCustomDraw = false;
         for (auto& custom : customDraws)
         {
            if (custom.customRect.Contains(io.MousePos))
            {
               overCustomDraw = true;
            }
         }
         if (overCustomDraw) {
         }
         else
         {
#if 0
            frameOverCursor = *firstFrame + (int)(visibleFrameCount * ((io.MousePos.x - (float)legendWidth - canvas_pos.x) / (canvas_size.x - legendWidth)));
            //frameOverCursor = max(min(*firstFrame - visibleFrameCount / 2, frameCount - visibleFrameCount), 0);

            /**firstFrame -= frameOverCursor;
            *firstFrame *= framePixelWidthTarget / framePixelWidth;
            *firstFrame += frameOverCursor;*/
            if (io.MouseWheel < -FLT_EPSILON)
            {
               *firstFrame -= frameOverCursor;
               *firstFrame = int(*firstFrame * 1.1f);
               framePixelWidthTarget *= 0.9f;
               *firstFrame += frameOverCursor;
            }

            if (io.MouseWheel > FLT_EPSILON)
            {
               *firstFrame -= frameOverCursor;
               *firstFrame = int(*firstFrame * 0.9f);
               framePixelWidthTarget *= 1.1f;
               *firstFrame += frameOverCursor;
            }
#endif
         }
      }

      if (expanded) {
         if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), !*expanded)) {
            *expanded = !*expanded;
         }
      }

      // if (delEntry != -1) {
      //    sequence->DeleteSequence(delEntry);
      //    if (selectedEntry && (*selectedEntry == delEntry || *selectedEntry >= sequence->GetLanesCount()))
      //       *selectedEntry = -1;
      // }

      // if (dupEntry != -1) {
      //    sequence->DuplicateSequence(dupEntry);
      // }
      return ret;
   }
}

