diff ClassPlan.lua @ 40:589c444d4837

WowAce/Curseforge migration push
author Nenue
date Sun, 25 Dec 2016 13:04:57 -0500
parents 26dfa661daa7
children 79e5e96e5f18
line wrap: on
line diff
--- a/ClassPlan.lua	Fri Nov 04 02:54:32 2016 -0400
+++ b/ClassPlan.lua	Sun Dec 25 13:04:57 2016 -0500
@@ -1,3 +1,4 @@
+local _, db = ...
 local wipe, tinsert, sort = table.wipe, tinsert, table.sort
 local pairs, ipairs = pairs, ipairs
 local floor, mod, time = floor, mod, time
@@ -6,20 +7,25 @@
 local GI_currentTime = time()
 local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop
 
-local CG_GetBuildings = C_Garrison.GetBuildings
-local CG_GetFollowerShipments = C_Garrison.GetFollowerShipments
-local CG_GetLooseShipments = C_Garrison.GetLooseShipments
-local CG_GetTalentTrees = C_Garrison.GetTalentTrees
-local CG_GetCompleteTalent = C_Garrison.GetCompleteTalent
-local CG_GetLandingPageShipmentInfo = C_Garrison.GetLandingPageShipmentInfo
-local CG_GetLandingPageShipmentInfoByContainerID = C_Garrison.GetLandingPageShipmentInfoByContainerID
-
 local CP_REPLACE_LANDINGPAGE = true
 local CP_HEADER_SIZE = 24
-local CP_BACKGROUND_COLOR = {
-  inProgress = {0, 0, 0, 0.5},
-  shipmentsReady = {0, 0, 0, 0.25},
-  complete = {0.5, 0.5, 0.5, 0.5}
+db.ClassPlanDefaultType = {
+
+  backgroundColor = {0, 0, 0, 0.5},
+  textColor = {1,1,1,1}
+}
+db.ClassPlanTypes = setmetatable({}, {__index = db.ClassPlanDefaultType})
+db.ClassPlanTypes.inProgress = {
+  backgroundColor = {0, 0, 0, 0.5},
+  textColor = {1,1,1}
+}
+db.ClassPlanTypes.shipmentsReady = {
+  backgroundColor = {1, 1, 0, 0.25 },
+  textColor = {1, 1, 0}
+}
+db.ClassPlanTypes.complete = {
+  backgroundColor = {0, 1, 0, 0.25 },
+  textColor = {0, 1, 0}
 }
 
 local GetTimeLeftString = function(timeLeft)
@@ -34,7 +40,6 @@
   end
 end
 
-
 ClassOrderPlanCore = {
   events = {},
   freeBlocks = {},
@@ -45,61 +50,72 @@
   shipments = {},
   playerFirst = false,
   prototypes = {},
-  Queued = {}
+  Queued = {},
+  Timers = {},
+  ReportChunks = {},
 }
-local MissionList = {
-  templateName = 'ClassPlanMissionEntry',
-  listKey = {'missions', 'available'},
-  listTitle = {'In Progress', 'Available'},
-
-  point = 'TOPLEFT',
-  relativePoint ='TOPLEFT',
-  events = {
-    'GARRISON_MISSION_LIST_UPDATE',
-    'GARRISON_LANDINGPAGE_SHIPMENTS'},
-}
-local ShipmentList = {
-  templateName = 'ClassPlanShipmentEntry',
-  listKey = {'shipments'},
-  listTitle = {'Work Orders'},
-  events = {
-    'GARRISON_MISSION_LIST_UPDATE',
-    'GARRISON_LANDINGPAGE_SHIPMENTS',
-    'GARRISON_TALENT_UPDATE',
-    "GARRISON_TALENT_COMPLETE",
-    "GARRISON_SHIPMENT_RECEIVED",
-    'GARRISON_FOLLOWER_LIST_UPDATE',
-    'GARRISON_SHOW_LANDING_PAGE'},
-}
-local SharedHandlers = {
+ClassPlanHandlerBase = {
   numBlocks = 0,
   isStale = true,
   maxItems = 10
 }
-local SharedEntry = {}
-local ShipmentEntry = {}
-local MissionEntry = {}
+ClassPlanEntryBase = {}
 
 local ClassPlan = ClassOrderPlanCore
+local Embed = function(object, ...)
+  for i = 1, select('#', ...) do
+    local src = select(i, ...)
+    for k,v in pairs(src) do
+      if not object[k] then
+        object[k] = v
+      end
+    end
+  end
+  return object
+end
 
 function ClassPlan:OnLoad ()
   self:RegisterEvent('PLAYER_LOGIN')
   self:RegisterEvent('ADDON_LOADED')
   self:RegisterEvent('PLAYER_REGEN_ENABLED')
   self:RegisterEvent('PLAYER_REGEN_DISABLED')
+  self:RegisterEvent('GARRISON_SHOW_LANDING_PAGE')
   self:RegisterForDrag('LeftButton')
   self:SetMovable(true)
   self:SetToplevel(true)
 
-
   SLASH_CLASSPLAN1 = "/classplan"
   SLASH_CLASSPLAN2 = "/cp"
   SlashCmdList.CLASSPLAN = function(args)
     self:Toggle()
   end
 
+  local originalScript = GarrisonLandingPageMinimapButton:GetScript('OnClick')
+  GarrisonLandingPageMinimapButton:SetScript("OnClick", function(minimap, button)
+    if button == 'LeftButton' and (not IsShiftKeyDown()) then
+      self:Toggle()
+    else
+      originalScript(minimap, button)
+    end
+  end)
+  self.FadeOut:SetScript('OnFinished', function()
+    self:Hide()
+    self.data.IsShown = nil
+    self.isAnimating = nil
+  end)
+  self.FadeIn:SetScript('OnPlay', function()
+    self.isAnimating = true
+    self:SetShown(true)
+  end)
+
+  --hooksecurefunc(C_Garrison, 'RequestLandingPageShipmentInfo', function()
+  --  WorldPlan:print("Requesting shipments data.")
+  --end)
+  C_Garrison.RequestLandingPageShipmentInfo();
+  self.isStale = true
 end
 
+
 function ClassPlan:GetCurrentProfile()
   WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
   local db = WorldPlanData.OrderHall
@@ -110,38 +126,49 @@
 
   local name, realm = UnitName('player')
   realm  = realm or GetRealmName()
+
   local profileName = name .. '-' .. realm
-
-  self.profile = characters[profileName] or {}
+  self.profile = characters[profileName] or {
+    showItems = true
+  }
   self.characters = characters
   characters[profileName] = self.profile
 
-
   local classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]
   local className = UnitClass('player')
-
   print('|cFFFFFF00Loaded:|r', classColor.hex, className, profileName)
-  self.Background:SetColorTexture(classColor.r, classColor.g, classColor.b, 0.5)
   self.profile.classColor = classColor
   self.profile.className = className
   self.profile.characterName = name
   self.profile.characterRealm = realm
+  -- flip it on
+  self.profile.showItems = true
+
+  self.HeaderInset:SetHeight(CP_HEADER_SIZE)
+  self.ClassStripe:SetColorTexture(classColor.r, classColor.g, classColor.b, 1)
+  self.ClassStripe:SetPoint('TOPLEFT', self.HeaderInset, 'BOTTOMLEFT')
+
   return self.profile
 end
 
-function ClassPlan:SetupHandler(handler)
-  print('|cFF00FF00'..handler:GetName()..' loaded')
-  for i, event in ipairs(handler.events) do
+function ClassPlan:AddHandler(frame)
+  print('|cFF00FF00'..frame:GetName()..' loaded')
+  for i, event in ipairs(frame.events) do
     print('|cFF00FF00  event', event)
-    handler:RegisterEvent(event)
+    frame:RegisterEvent(event)
   end
-  for index, listKey in ipairs(handler.listKey) do
+  frame.sortedItems = {}
+  for index, listKey in ipairs(frame.listKey) do
+    frame.profile = self.profile
+    frame.data = self.data
     self.profile[listKey] = self.profile[listKey] or {}
-    local listTitle = handler.listTitle[index]
-    setmetatable(self.profile[listKey], { __tostring = listTitle })
+    local listTitle = frame.listTitle[index]
+    setmetatable(self.profile[listKey], { __tostring = function() return listTitle end })
+    frame.sortedItems[listKey] = {}
+
   end
-  handler:SetList(1)
-  handler.sortedItems = {}
+  frame.owningFrame = self
+  frame:SetList(1)
 end
 
 function ClassPlan:OnEvent (event, arg)
@@ -165,6 +192,8 @@
     if not self.initialized then
       self:Setup()
     end
+  elseif event == 'GARRISON_SHOW_LANDING_PAGE' then
+    self:RefreshData()
   end
 end
 
@@ -174,120 +203,124 @@
 
     self:GetCurrentProfile()
     for _, handler in ipairs(self.Handlers) do
-      self:SetupHandler(handler)
+      self:AddHandler(handler)
+      handler.initialized = true
     end
     self.initialized = true
     self:SetShown(self.data.IsShown)
+
+
   end
 end
 
 
 --- Update space
 
-  local max = math.max
-  function ClassPlan:Update()
-    print('|cFF00FFFFRefresh()|r')
-    self.currentHeight = 0
-    for index, handler in pairs(self.Handlers) do
-      if handler.isStale then
-        print('  |cFF00FF00'..index..' '..handler:GetName()..'|r')
-        local sortedItems = handler.sortedItems
-        local activeKey = handler.activeKey
+local max = math.max
+function ClassPlan:RefreshData()
+  local detailsFailed
+  for index, handler in pairs(self.Handlers) do
+    print(' |cFF00FF00'..handler:GetName()..' data update|r')
+    handler:RefreshData()
+  end
+  if detailsFailed then
+    db.print('Unable to obtain player details. Trying again later.')
+  else
+    self.requestingData = nil
+  end
+end
 
-        handler.profile = self.profile[handler.activeKey]
-        handler.currentTime = GI_currentTime
-        handler:GetPlayerData(self.profile)
-        wipe(sortedItems)
-        for key, profile in pairs(self.data.characters) do
-          print('profile', key, activeKey)
-          local profileList = profile[activeKey]
-          if profileList and #profileList >= 1 then
-            local classColor = profile.classColor or RAID_CLASS_COLORS['HUNTER']
-            local isMine = (profile == self.profile)
-            for index, data in ipairs(profileList) do
-              data.classColor = classColor
-              data.profileKey = key
-              data.isMine = isMine
-              if handler.OnGetItem then
-                handler:OnGetItem(data)
-              end
-              tinsert(sortedItems, data)
-            end
-          end
-        end
+function ClassPlan:Update()
+  print('|cFF00FFFF'..self:GetName()..'Refresh()|r')
 
-        if handler.SortHandler then
-          sort(sortedItems, handler.SortHandler)
-        end
+  self.currentHeight = 0
+  for index, handler in ipairs(self.Handlers) do
+    local itemsHeight = handler:UpdateItems()
+    if itemsHeight then
+      self.currentHeight = max(itemsHeight, self.currentHeight)
+    end
+  end
 
-      end
-      handler.isStale = nil
-      local itemsHeight = handler:UpdateItems()
-      self.currentHeight = max(itemsHeight, self.currentHeight)
-
+  local index = 1
+  for key, info in pairs(self.data.characters) do
+    print('cbutton', key)
+    if self.data[key] then
+      print('|cFFFF4400remove legacy value', key)
+      self.data[key] = nil
     end
 
-    local index = 1
-    for id, profile in pairs(self.data.characters) do
-      local button = self.characterButtons[index]
-      if not button then
-        button = CreateFrame('Button', nil, self, 'ClassOrderPlanCharacterButton')
-        button:SetID(index)
-        self.characterButtons[index] = button
 
-        if not self.lastButton then
-          button:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', 0, 0)
-        else
-          button:SetPoint('BOTTOMLEFT', self.lastButton, 'BOTTOMRIGHT', 2, 0)
-        end
-        self.lastButton = button
+    local button = self.characterButtons[index]
+    if not button then
+      button = CreateFrame('Button', nil, self, 'ClassOrderPlanCharacterButton')
+      button:SetID(index)
+      self.characterButtons[index] = button
+
+      if not self.lastButton then
+        button:SetPoint('TOPLEFT', self.HeaderInset, 'TOPLEFT', 0, 0)
+      else
+        button:SetPoint('TOPLEFT', self.lastButton, 'TOPRIGHT', 2, 0)
       end
-      if not profile.characterName then
-        profile.characterName, profile.characterRealm = id:match("%(.+)%-(.+)^")
-      end
-
-      button.className = profile.className
-      button.classColor = profile.classColor
-      button.characterName = profile.characterName
-      button.characterRealm = profile.characterRealm
-      button.hideItems = (profile.showItems == false) and (profile ~= self.profile)
-      button.isMine = (profile == self.profile)
-      button:Update()
-      button:Show()
-      index = index + 1
+      self.lastButton = button
+    end
+    if not info.characterName then
+      info.characterName, info.characterRealm = key:match("%(.+)%-(.+)^")
     end
 
+    print(info.characterName)
 
-    self.isStale = nil
-    self:Reanchor()
-    self:SetHeight(self.currentHeight + CP_HEADER_SIZE)
+    button:SetSize(CP_HEADER_SIZE, CP_HEADER_SIZE)
+    button.profileKey = key
+    button.className = info.className
+    button.classColor = info.classColor
+    button.characterName = info.characterName
+    button.characterRealm = info.characterRealm
+    button.showItems = info.showItems
+    button.isMine = (info == self.profile)
+    button:Update()
+    button:Show()
+    index = index + 1
   end
 
+  self.HeaderInset:SetHeight(CP_HEADER_SIZE)
+  self.HeaderInset:ClearAllPoints()
+  self.HeaderInset:SetPoint('TOPLEFT' ,self, 'TOPLEFT')
+  self.HeaderInset:SetPoint('RIGHT' ,self, 'RIGHT')
+  self.ClassStripe:ClearAllPoints()
+  self.ClassStripe:SetPoint('TOPLEFT', self.HeaderInset, 'BOTTOMLEFT', 0, 0)
+  self.ClassStripe:SetPoint('RIGHT')
+  self:Reanchor()
+  self.isStale = nil
+end
+
 
 function ClassPlan:Toggle()
   if self:IsShown() then
-    self:Hide()
+    self.FadeOut:Play()
   else
-    self:Show()
-  end
-
-  if self.data then
-    self.data.IsShown = self:IsShown()
+    self.data.IsShown = true
+    self.FadeIn:Play()
   end
 end
 
+
 function ClassPlan:OnUpdate()
-  if self.isStale then
-    print('|cFFFF4400An illusion! What are you hiding?|r')
+  if self.requestingData then
+    self:RefreshData()
+  elseif self.isStale then
+    -- shouldn't happen, usually
     self:Update()
   end
+  if #self.ReportChunks >= 1 then
+    db.print(table.concat(self.ReportChunks, ', '))
+    wipe(self.ReportChunks)
+  end
+
 end
 
 function ClassPlan:OnShow()
   print('|cFF00FFFFShow()')
-  if self.isStale then
-    self:Update()
-  end
+  self.isStale = true
   self:Reanchor()
 end
 
@@ -296,30 +329,34 @@
 end
 
 function ClassPlan:Reanchor()
+  if not (self.data.positionX and self.data.positionY) then
+    self.data.positionX = 0
+    self.data.positionY = -148
+  end
   self:ClearAllPoints()
-  self:SetPoint('CENTER', self.data.positionX, self.data.positionY)
-
+  self:SetPoint('TOP', self.data.positionX, self.data.positionY)
+  self.currentHeight = 0
   for index, frame in ipairs(self.Handlers) do
     frame:Reanchor()
-
     local ListTab = frame.ListTab
     if ListTab then
       ListTab:ClearAllPoints()
-      ListTab:SetPoint('TOPLEFT', frame, 'TOPLEFT', 0, CP_HEADER_SIZE)
-      ListTab:SetPoint('BOTTOMRIGHT', frame, 'TOPRIGHT', 0, 0)
+      ListTab:SetPoint('TOPLEFT', frame, 'TOPLEFT', 0, 0)
+      ListTab:SetPoint('BOTTOMRIGHT', frame, 'TOPRIGHT', 0, -CP_HEADER_SIZE)
       ListTab.Label:SetText(frame.listTitle[frame.currentListIndex])
       ListTab:Show()
       print(ListTab:GetSize())
     end
+    self.currentHeight = max(self.currentHeight, frame.currentHeight or 0)
+  end
+  self:SetHeight(self.currentHeight + self.HeaderInset:GetHeight() + self.ClassStripe:GetHeight())
 
-  end
 end
 
 function ClassPlan:OnDragStart()
   self:StartMoving()
 end
 function ClassPlan:OnDragStop()
-
   self:StopMovingOrSizing()
   local x,y = self:GetCenter()
   if x and y then
@@ -330,7 +367,23 @@
   end
 end
 
-function SharedHandlers:SetList(index)
+function ClassPlanHandlerBase:ScheduleUpdate(expires)
+  -- value will occasionally lag so check here
+  local duration = expires - time()
+  self.Timers = self.Timers or {}
+  if (duration > 0) and  (not self.Timers[expires]) then
+    print('  adding timer at', expires, 'c', duration)
+    self.Timers[expires] = true
+    C_Timer.After(duration, function()
+      self.isStale = true
+      self:UpdateItems()
+      self.Timers[expires] = nil
+    end)
+  end
+end
+
+function ClassPlanHandlerBase:SetList(index)
+  local prevIndex = self.currentListIndex
   if not index then
     if self.currentListIndex == #self.listKey then
       index = 1
@@ -339,57 +392,93 @@
     end
   end
 
-  print('|cFF0088FF'..self:GetName()..'|r:SetList()', index)
+  print('|cFF0088FF'..self:GetName()..'|r:SetList()', index, self.listKey[index])
   self.currentListIndex = index
   self.activeKey = self.listKey[index]
   self.activeTitle = self.listTitle[index]
 
+  self.ListTab.Label:SetText(self.listTitle[index])
   self.isStale = true
+
+  if self.OnSetList then
+    self:OnSetList(self.currentListIndex, prevIndex)
+  end
 end
 
-function SharedHandlers:OnMouseWheel(delta)
+function ClassPlanHandlerBase:OnMouseWheel(delta)
   self.scrollOffset = (self.scrollOffset or 0) - ((delta > 0) and 1 or -1)
   self:UpdateItems()
 end
 
-function SharedHandlers:RequestData()
-  print('|cFF0088FF'..self:GetName()..':RequestData()')
+function ClassPlanHandlerBase:RefreshData()
+  print('|cFF0088FF'..self:GetName()..':RefreshData()')
+  local activeKey = self.activeKey
+  local detailsFailed
+  self.truncatedItems = 0
+  self.currentTime = time()
+  if self:GetParent().profile then
+    self:GetPlayerData()
+  else
+    detailsFailed  = true
+  end
+  for _, listKey in ipairs(self.listKey) do
+    local sortedItems = self.sortedItems[listKey]
+    wipe(sortedItems)
+    for key, profile in pairs(self.data.characters) do
+      local isMine = (profile == self.profile)
+      print(key, listKey, isMine, profile.showItems)
+      local profileList = profile[listKey]
+      if profileList and #profileList >= 1 then
+        local classColor = profile.classColor or RAID_CLASS_COLORS['HUNTER']
+        if profile.showItems then
+          for index, data in ipairs(profileList) do
+            data.classColor = classColor
+            data.profileKey = key
+            data.isMine = isMine
+            if self.OnGetItem then
+              self:OnGetItem(data)
+            end
+            tinsert(sortedItems, data)
+          end
+        else
+          self.truncatedItems = self.truncatedItems + 1
+        end
+      end
+    end
+
+    if self.SortHandler then
+      sort(sortedItems, self.SortHandler)
+    end
+  end
+
+  for k,v in pairs(self.sortedItems) do
+    print('  ', k)
+  end
+
   self.isStale = true
 end
 
-function SharedHandlers:OnEvent(event, arg)
-  if (event == 'GARRISON_MISSION_LIST_UPDATE') and (arg ~= LE_FOLLOWER_TYPE_GARRISON_7_0) then
-    -- ignore non-OrderHall updates
-    return
-  end
-  print('|cFF00FF88'..self:GetName()..':OnEvent()|r', event, arg)
-  if self:IsVisible() then
-    print('|cFF88FF00  frame visible; get busy')
-    self:RequestData()
+function ClassPlanHandlerBase:OnEvent(event, id)
+  if (event == 'GARRISON_MISSION_LIST_UPDATE') then
+    if (id == LE_FOLLOWER_TYPE_GARRISON_7_0) then
+      print('|cFF00FF88'..self:GetName()..':OnEvent()|r', event, id)
+      self:RefreshData()
+    end
   else
-    if not self.NextData then
-      print('|cFF88FF00  setting timer')
-      self.NextData = C_Timer.NewTimer(0.25, function()
-        if self.initialized then
-          self:RequestData()
-          self.NextData:Cancel()
-          self.NextData = nil
-          print('|cFF88FF00'..self:GetName()..' clearing timer')
-        end
-
-      end)
-    end
-  end
-end
-function SharedHandlers:OnUpdate()
-  if self.isStale then
-    self:GetParent():Update()
+    print('|cFF00FF88'..self:GetName()..':OnEvent()|r', event, id)
   end
 end
 
+function ClassPlanHandlerBase:OnUpdate()
+  if self.isStale then
+    print('|cFF00FF00'..self:GetName()..':OnUpdate()|r')
+
+    self:UpdateItems()
+  end
+end
 
 -- Stuff set on every list item
-function SharedHandlers:SetOwnerData (self, data)
+function ClassPlanHandlerBase:SetOwnerData (self, data)
   local name, realm = string.match(data.profileKey, "(.+)%-(.+)")
   local ownerText = '|c'.. data.classColor.colorStr .. name .. '|r'
   self.Owner:SetText(ownerText)
@@ -397,17 +486,16 @@
   self.Name:SetTextColor(data.classColor.r, data.classColor.g, data.classColor.b)
 end
 
-function SharedHandlers:Acquire(id)
+function ClassPlanHandlerBase:Acquire(id)
 end
-function SharedHandlers:FreeBlock (block)
+function ClassPlanHandlerBase:FreeBlock (block)
 end
 
-function SharedHandlers:UpdateItems()
-
+function ClassPlanHandlerBase:UpdateItems()
+  print('|cFF0088FF '..self:GetName()..':UpdateItems()|r', self.activeKey)
   self.MoreItemsUp:Hide()
   self.MoreItemsDown:Hide()
-
-  local sortedItems = self.sortedItems
+  local sortedItems = self.sortedItems[self.activeKey]
   local scrollOffset = self.scrollOffset or 0
   local numItems = #sortedItems
   if (not sortedItems[scrollOffset+1]) or (numItems <= self.maxItems) then
@@ -416,41 +504,41 @@
     scrollOffset = (numItems - self.maxItems)
   end
 
-
+  self.ListTab.Count:SetText(numItems)
   self.blocks = self.blocks or  {}
   local blocks = self.blocks
-
   local lastProfile
-  local totalHeight = 0
+  local totalHeight = (self.ListTab:GetHeight() or 0)
   self.lastBlock = nil
   self.numActive = 0
   for i = 1, self.maxItems do
     local index = scrollOffset + i
     local data = sortedItems[index]
     if not data then
+      print('|cFFFF4400end of data')
       break
     end
 
-
     local block = blocks[i]
     if not block then
-      block = CreateFrame('Button', nil, self, self.templateName)
+      block = CreateFrame('Button', self:GetName()..'ListItem'..i , self, self.templateName)
       block.listType = self.activeKey
+
+      block.doAnimation = true
+
       block.handler = self
       self.numBlocks = self.numBlocks + 1
       blocks[i] = block
     end
     block:SetID(index)
-
-    print('RefreshItem', block)
     self.numActive = self.numActive + 1
 
     if self.lastBlock then
       block:SetPoint('TOPLEFT', self.lastBlock, 'BOTTOMLEFT', 0, 0)
-      print('--', index, data.isComplete, data.missionEndTime, data.name)
+      --print('--', index, data.isComplete, data.missionEndTime, data.name)
     else
-      block:SetPoint('TOPLEFT', 0, 0)
-      print('--top')
+      block:SetPoint('TOPLEFT', self.ListTab, 'BOTTOMLEFT', 0, 0)
+      --print('--top')
     end
     self.lastBlock = block
 
@@ -491,104 +579,33 @@
     end
   end
 
+  self:Reanchor()
+  if totalHeight ~= self.currentHeight then
+    self.currentHeight = totalHeight
+    self:SetHeight(self.currentHeight)
+    self:GetParent():Reanchor()
+  end
+
+
   self.scrollOffset = scrollOffset
-  self:Reanchor()
-
+  self.isStale = nil
   return totalHeight
 end
 
+function ClassPlanEntryBase:OnAnimFinished()
+end
+function ClassPlanEntryBase:OnShow()
 
-function ShipmentList:Reanchor()
-  print('|cFF00FFFF'..self:GetName()..':Reanchor|r')
-  self:SetPoint('TOPLEFT', 0, -24)
-  self:SetPoint('BOTTOMRIGHT', -ClassOrderPlan:GetWidth()/2, 0)
-end
-
-
-
-do
-  local ShipmentsInfo = {}
-  local AddShipmentInfo = function(shipmentType, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID, followerID)
-    -- early login queries may return empty tables, causing the sorter to compare nil
-    if not creationTime then
-      return
+  print('|cFF44FF00'..self:GetName()..':OnShow()')
+  if self.doAnimation then
+    self.doAnimation = nil
+    if not ClassOrderPlan.isAnimating then
+      self.NewBlockFade:Play()
     end
-    --print(shipmentType, name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
-    tinsert(ShipmentsInfo,
-      {
-        shipmentType = shipmentType,
-        name = name,
-        icon = texture,
-        shipmentCapacity = shipmentCapacity,
-        shipmentsReady = shipmentsReady,
-        shipmentsTotal = shipmentsTotal,
-        creationTime = creationTime,
-        duration = duration,
-        timeleftString = timeleftString,
-        itemName = itemName,
-        itemIcon = itemIcon,
-        itemQuality = itemQuality,
-        itemID = itemID,
-        followerID = followerID,
-      })
-  end
-  function ShipmentList:GetPlayerData (profile)
-    if not profile then
-      return false
-    end
-    local profileList = profile.shipments
-    wipe(ShipmentsInfo)
-
-    local garrisonType = LE_GARRISON_TYPE_7_0
-    local buildings = CG_GetBuildings(garrisonType);
-    local shipmentIndex = 0
-    --print('Buildings:')
-    for i = 1, #buildings do
-      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = CG_GetLandingPageShipmentInfo(buildingID);
-      AddShipmentInfo('Building', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID)
-    end
-
-    --print('Follower:')
-    local followerShipments = CG_GetFollowerShipments(garrisonType);
-    for i = 1, #followerShipments do
-      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = CG_GetLandingPageShipmentInfoByContainerID(followerShipments[i]);
-      AddShipmentInfo('Follower', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, nil, nil, nil, nil, followerID)
-    end
-
-    --print('Loose:')
-    local looseShipments = CG_GetLooseShipments(garrisonType)
-    for i = 1, #looseShipments do
-      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = CG_GetLandingPageShipmentInfoByContainerID(looseShipments[i]);
-      AddShipmentInfo('Misc', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
-    end
-
-    local talentTrees = CG_GetTalentTrees(garrisonType, select(3, UnitClass("player")));
-    -- this is a talent that has completed, but has not been seen in the talent UI yet.
-    local completeTalentID = CG_GetCompleteTalent(garrisonType);
-    --print('Talents:')
-    if (talentTrees) then
-      for treeIndex, tree in ipairs(talentTrees) do
-        for talentIndex, talent in ipairs(tree) do
-          local showTalent = false;
-          if (talent.isBeingResearched) or (talent.id == completeTalentID) then
-            AddShipmentInfo('Talent', talent.name, talent.icon, 1, (talent.isBeingResearched and 0 or 1), 1, talent.researchStartTime, talent.researchDuration, talent.timeleftString)
-          end
-        end
-      end
-    end
-
-    wipe(profileList)
-    for index, data in ipairs(ShipmentsInfo) do
-      --DEFAULT_CHAT_FRAME:AddMessage(data.shipmentType ..' '.. tostring(data.name) ..' '.. tostring(data.creationTime) ..' '.. tostring(data.duration))
-      tinsert(profileList, data)
-    end
-    self.isStale = true
-    return true
   end
 end
 
-
-function SharedEntry:SetTimeLeft(expires, duration)
+function ClassPlanEntryBase:SetTimeLeft(expires, duration)
   self.ProgressBG:Hide()
   self.ProgressBar:Hide()
   if not expires then
@@ -596,10 +613,14 @@
   end
 
   -- calculate here since time isn't available
-  local timeLeft = expires - GI_currentTime
+  local timeLeft = expires - time()
   if timeLeft < 0 then
     -- handle being complete
-
+    if self.shipmentsReady and (self.shipmentsReady < self.shipmentsTotal) then
+      self.TimeLeft:SetText('Ready')
+    else
+      self.TimeLeft:SetText('Complete!')
+    end
   else
     self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
   end
@@ -610,188 +631,85 @@
     local g = ((progress <= .5) and (progress*2)) or 1
     self.ProgressBG:Show()
     self.ProgressBar:Show()
-
     self.ProgressBG:SetColorTexture(r,g,0,0.25)
     self.ProgressBar:SetColorTexture(r,g,0,0.5)
-    self.ProgressBar:SetWidth(self:GetWidth() * progress)
+    self.ProgressBar:SetWidth(self.ProgressBG:GetWidth() * progress)
   end
 end
 
--- Update shipment flags data
-local SetActualShipmentTime = function(self)
 
-  if self.isComplete then
-    return nil, nil
+
+ClassPlanHeaderMixin = {}
+function ClassPlanHeaderMixin:OnLoad()
+    self:EnableMouse((#self:GetParent().listKey > 1))
+    self:RegisterForClicks('AnyUp')
   end
-
-  local timestamp = time()
-  local timeLeft = self.creationTime + self.duration - timestamp
-  local duration = self.duration * self.shipmentsTotal
-  local justFinished = false
-  while (self.shipmentsReady < self.shipmentsTotal) and (timeLeft <= 0) do
-    if not self.originalReady then
-      self.originalReady = self.shipmentsReady
-      self.originalCreationTime = self.creationTime
-    end
-
-
-    self.shipmentsReady = self.shipmentsReady + 1
-    self.creationTime = self.creationTime + self.duration
-    timeLeft = timeLeft + self.duration
-    print('|cFF00FF88udpating '..self.name..'|r', 'timeLeft:', timeLeft, 'shipments:', self.shipmentsReady, self.shipmentsTotal)
-  end
-
-  if (timeLeft <= 0) and (not self.isBeingResearched) then
-    self.isComplete = true
-    self.isStale = true
-  end
-
-
-  local expires = (self.originalCreationTime or self.creationTime) + duration
-
-  return expires, duration
-end
-
-function ShipmentList:OnGetItem (data)
-  print('OnGetItem()')
-  if data.shipmentsTotal then
-    SetActualShipmentTime(data)
+function ClassPlanHeaderMixin:OnClick ()
+  local frame = self:GetParent()
+  frame:SetList()
+  if frame.OnHeaderClick then
+    frame.OnHeaderClick(frame)
   end
 end
 
-ShipmentList.SortHandler = function(a, b)
-  if b.isComplete ~= a.isComplete then
-    return a.isComplete and true or false
-  elseif a.shipmentsReady or b.shipmentsReady then
-    return (a.shipmentsReady or 0) > (b.shipmentsReady or 0)
-  else
-    return (a.creationTime) < (b.creationTime)
+ClassPlanCharacterButtonMixin = {
+}
+function ClassPlanCharacterButtonMixin:Update ()
+  --print(CLASS_ICON_TCOORDS[self.className:upper()])
+  if self.className and CLASS_ICON_TCOORDS[self.className:upper()] then
+    self.Icon:SetTexCoord(unpack(CLASS_ICON_TCOORDS[self.className:upper()]))
   end
+  self.Icon:SetDesaturated((not self.showItems))
+  self.SelectGlow:SetShown(self.isMine)
 end
 
-function ShipmentList:OnLoad()
-  C_Garrison.RequestLandingPageShipmentInfo();
-end
-function  ShipmentList:OnShow()
-  print('|cFF00FF88'..self:GetName()..':OnShow()|r')
-  C_Garrison.RequestLandingPageShipmentInfo()
-end
-
-function ShipmentEntry:OnLoad()
-  MissionEntry.OnLoad(self)
-end
-
-
-function ShipmentEntry:Update()
-  print('|cFF0088FF'.. self.name..'|r:Update()')
-  self.Icon:SetTexture(self.icon)
-  self.Count:SetText(self.shipmentsReady)
-  self.Done:SetShown(self.shipmentsReady and (self.shipmentsReady >= 1))
-
-  -- flag as complete
-
-  local bgColor = CP_BACKGROUND_COLOR.inProgress
-  if ( self.shipmentsReady >= self.shipmentsTotal ) and (not self.isBeingResearched) then
-    self.Swipe:SetCooldownUNIX(0, 0);
-    self.Done:Show();
-    bgColor = CP_BACKGROUND_COLOR.complete
-  else
-    if (self.shipmentsReady >= 1) and (self.shipmentsReady < self.shipmentsTotal) then
-      bgColor = CP_BACKGROUND_COLOR.shipmentsReady
-    end
-    self.Swipe:SetCooldownUNIX(self.creationTime or 0 , self.duration or 0);
-  end
-  self.Background:SetColorTexture(unpack(bgColor))
-
-  SetActualShipmentTime(self)
-
-  if self.originalReady then
-    print('|cFF00FF88'..self.name..'|r', 'starting ready:', self.originalReady, 'starting time:', self.originalCreationTime)
-  end
-end
-
-function ShipmentEntry:OnUpdate(sinceLast)
-  self.throttle = (self.throttle or 1) + sinceLast
-  if self.throttle >= 1 then
-    self.throttle = self.throttle - 1
-  else
+function ClassPlanCharacterButtonMixin:OnEnter()
+  if not self.profileKey then
     return
   end
 
-
-  if (self.shipmentsReady and self.shipmentsTotal) and (self.shipmentsReady < self.shipmentsTotal) then
-    local expires, duration = SetActualShipmentTime(self)
-
-    if self.isComplete then
-      self.TimeLeft:SetText('Complete!')
-      self.TimeLeft:SetTextColor(0,1,1)
-    elseif self.shipmentsReady >= 1 then
-      self:SetTimeLeft(expires, duration)
-      self.TimeLeft:SetTextColor(0,1,0)
-    else
-      self:SetTimeLeft(expires, duration)
-      self.TimeLeft:SetTextColor(1,1,1)
-    end
-
-  elseif self.isBeingResearched then
-    self:SetTimeLeft(self.researchStartTime + self.researchDuration - time(), self.researchDuration)
-    self.TimeLeft:SetTextColor(1,1,1)
-  else
-    self.TimeLeft:SetText('Complete!')
-    self.TimeLeft:SetTextColor(0,1,0)
+  GameTooltip:SetOwner(self, 'ANCHOR_RIGHT')
+  local info = ClassOrderPlan.data.characters[self.profileKey]
+  GameTooltip:AddLine(self.characterName, self.classColor.r, self.classColor.g, self.classColor.b)
+  local numItems = 0
+  if info.missions then
+    GameTooltip:AddLine(#info.missions .. ' mission'..((#info.missions == 1) and '' or 's')..' in progress')
   end
-
+  if info.shipments then
+    GameTooltip:AddLine(#info.shipments .. ' work order' .. ((#info.shipments == 1) and '' or 's'))
+  end
+  if info.available then
+    GameTooltip:AddLine(#info.available .. ' mission'..((#info.available == 1) and '' or 's')..' available')
+  end
+  GameTooltip:Show()
 end
 
-function ShipmentEntry:OnEnter()
-  if ( self.shipmentsReady and self.shipmentsTotal ) then
-    GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
-    GameTooltip:AddLine(self.Owner:GetText(), self.Owner:GetTextColor())
-    GameTooltip:AddLine(self.shipmentType)
-    GameTooltip:AddLine(self.shipmentsReady .. ' of '.. self.shipmentsTotal)
-    GameTooltip:Show()
-  end
-end
-
-function ShipmentEntry:OnLeave()
+function ClassPlanCharacterButtonMixin:OnLeave()
   if GameTooltip:IsOwned(self) then
     GameTooltip:Hide()
   end
 end
 
-function ShipmentEntry:OnClick(button)
+function ClassPlanCharacterButtonMixin:OnClick(button, down)
+  print('OnClick', self.profileKey)
+  local clist = ClassOrderPlan.data.characters
+
   if button == 'RightButton' then
-    self.handler:FreeBlock(self)
+    for _, profile in pairs(clist) do
+      profile.showItems = true
+    end
+  else
+    if clist[self.profileKey].showItems then
+      clist[self.profileKey].showItems = nil
+    else
+      clist[self.profileKey].showItems = true
+    end
   end
+  for i, handler in ipairs(ClassOrderPlan.Handlers) do
+    handler.isStale = true
+  end
+
+  ClassOrderPlan:RefreshData()
+  ClassOrderPlan:Update()
+  print(clist[self.profileKey].showItems)
 end
-
-
-
-
-ClassPlanMissionHandler = Mixin(MissionList, SharedHandlers)
-ClassPlanMissionEntryMixin = Mixin(MissionEntry, SharedEntry)
-ClassPlanShipmentHandler = Mixin(ShipmentList, SharedHandlers)
-ClassPlanShipmentEntryMixin = Mixin(ShipmentEntry,SharedEntry)
-
-ClassPlanHeaderMixin = {
-  OnClick = function(self)
-    self:GetParent():SetList()
-    self:GetParent().isStale = true
-    ClassOrderPlan:Update()
-  end
-}
-
-ClassPlanCharacterButtonMixin = {
-  Update = function(self)
-    print(CLASS_ICON_TCOORDS[self.className:upper()])
-    if self.className and CLASS_ICON_TCOORDS[self.className:upper()] then
-      self.Icon:SetTexCoord(unpack(CLASS_ICON_TCOORDS[self.className:upper()]))
-    end
-    self.Icon:SetDesaturated(self.showItems)
-    self.SelectGlow:SetShown(self.isMine)
-  end
-}
-
-function ClassPlanCharacterButtonMixin:OnEnter() end
-function ClassPlanCharacterButtonMixin:OnLeave() end
-function ClassPlanCharacterButtonMixin:OnClick() end
\ No newline at end of file