diff ClassPlan.lua @ 3:c006ce87a147

prototype structure
author Nenue
date Sat, 15 Oct 2016 09:54:56 -0400
parents b8a19781f79b
children 4c7e9efec4b5
line wrap: on
line diff
--- a/ClassPlan.lua	Thu Oct 13 09:08:38 2016 -0400
+++ b/ClassPlan.lua	Sat Oct 15 09:54:56 2016 -0400
@@ -1,6 +1,9 @@
 local wipe = table.wipe
 local pairs, ipairs = pairs, ipairs
 local GetTime = GetTime
+local GI_currentTime = time()
+
+
 local blockTemplate = {
   point = 'TOPLEFT',
   relativePoint ='TOPLEFT',
@@ -9,260 +12,98 @@
 SLASH_CLASSPLAN1 = "/classplan"
 SLASH_CLASSPLAN2 = "/cp"
 SlashCmdList.CLASSPLAN = function(args)
-  if ClassOrderPlan:IsVisible() then
-    ClassOrderPlan:Hide()
-  else
-    ClassOrderPlan:Show()
-    DEFAULT_CHAT_FRAME:AddMessage('|cFF88FF00WorldPlan|r: Order Hall Panel')
+  ClassOrderPlan:Toggle()
+end
+
+ClassOrderPlanCore = {
+  events = {},
+  freeBlocks = {},
+  blocks = {},
+  sortedItems = {},
+  timers = {},
+  shipments = {},
+  playerFirst = false,
+  prototypes = {}
+}
+ClassPlanBlockMixin = {
+  templateName = 'ClassPlanBlock',
+  events = {'GARRISON_MISSION_LIST_UPDATE', 'GARRISON_MISSION_FINISHED', 'GARRISON_MISSION_FINISHED'},}
+ClassPlanShipmentMixin = {
+  templateName = 'ClassPlanShipment',
+  parent = false,
+  point = 'TOPRIGHT',
+  relativePoint ='TOPRIGHT',
+  events = {'GARRISON_LANDINGPAGE_SHIPMENTS', 'GARRISON_TALENT_UPDATE', "GARRISON_TALENT_COMPLETE", 'GARRISON_TALENT_COMPLETE', 'GARRISON_SHIPMENT_RECEIVED', "GARRISON_SHIPMENT_RECEIVED"},
+}
+setmetatable(ClassPlanShipmentMixin, {__index = ClassPlanBlockMixin})
+local core, MissionsHandler, ShipmentsHandler = ClassOrderPlanCore, ClassPlanBlockMixin, ClassPlanShipmentMixin
+local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop
+
+
+
+MissionsHandler.GetPlayerData = function(self)
+  if not self.profile then
+    return
+  end
+  self.items = C_Garrison.GetLandingPageItems(LE_GARRISON_TYPE_7_0)
+
+  if #self.items >= 1 then
+    wipe(self.profile.missions)
+    for index, data in ipairs(self.items) do
+      print('  ',data.name, '|cFF00FF00'.. data.timeLeft .. '|r', date("%A %I:%m %p", data.missionEndTime))
+      tinsert(self.profile.missions, data)
+    end
+    print('items update pending')
+    self.isStale = true
+  end
+
+  if self:IsVisible() then
+    self:Refresh()
   end
 end
 
-ClassOrderPlanCore = {
-  freeBlocks = {},
-  blocks = {},
-  shipmentBlocks = {},
-  freeShipmentBlocks = {},
-  sortedShipments = {},
-  sortedMissions = {},
-  timers = {},
-  shipments = {},
-  playerFirst = false,
-  templates = setmetatable({}, {
-    __newindex = function(t,k ,v)
-      if type(v) == 'table' then
-        setmetatable(v, {__index = blockTemplate})
-        rawset(t,k,v)
+MissionsHandler.SortHandler = function (a,b)
+  local result = false
+  if not a or not b then
+    result = true
+  else
+    if (a.isMine ~= b.isMine) then
+      result = a.isMine
+    else
+      if (not b.missionEndTime) or (not a.missionEndTime) then
+        print('missing article', b.missionEndTime, a.missionEndTime)
       end
+      result = ( b.missionEndTime > a.missionEndTime)
     end
-  })
-}
-ClassPlanBlockMixin = {}
-ClassPlanShipmentMixin = setmetatable({}, {__index = ClassPlanBlockMixin})
-local core, block, shipment = ClassOrderPlanCore, ClassPlanBlockMixin, ClassPlanShipmentMixin
-local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop
-
-
-
-function core:OnLoad ()
-  self:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player')
-  self:RegisterEvent('PLAYER_LOGIN')
-  self:RegisterEvent('PLAYER_ENTERING_WORLD')
-  self:RegisterEvent('PLAYER_REGEN_ENABLED')
-
-  self:RegisterEvent('GARRISON_MISSION_LIST_UPDATE')
-  self:RegisterEvent('GARRISON_MISSION_FINISHED')
-  self:RegisterEvent("GARRISON_LANDINGPAGE_SHIPMENTS");
-  self:RegisterEvent("GARRISON_SHIPMENT_RECEIVED");
-  self:RegisterEvent("GARRISON_TALENT_UPDATE");
-  self:RegisterEvent("GARRISON_TALENT_COMPLETE");
+  end
+  return result
 end
 
-core.templates.ClassPlanBlock = {
-  SetItemData = function(block, data)
-    block.isComplete = data.isComplete
-    block.missionEndTime = data.missionEndTime
-  end
-}
 
-core.templates.ClassPlanShipment = {
+ShipmentsHandler.OnGetItem = function(data)
+  if data.shipmentsTotal then
+    local timeLeft = data.creationTime + data.duration - GI_currentTime
+    if (timeLeft <= 0) and (data.shipmentsReady < data.shipmentsTotal) then
+      local numOrders = -1*floor(timeLeft/data.duration)
 
-  parent = false,
-  point = 'TOPRIGHT',
-  relativePoint ='TOPRIGHT',
-  SetItemData = function(block, data)
-    block.icon = data.icon
-    block.shipmentCapacity = data.shipmentCapacity
-    block.shipmentsReady = data.shipmentsReady
-    block.shipmentsTotal = data.shipmentsTotal
-    block.creationTime = data.creationTime
-    block.duration = data.duration
-    block.itemID = data.itemID
-    block.itemQuality = data.itemQuality
-    --[[
-        icon = texture,
-        shipmentCapacity = shipmentCapacity,
-        shipmentsReady = shipmentsReady,
-        shipmentsTotal = shipmentsTotal,
-        creationTime = creationTime,
-        duration = duration,
-        timeleftString = timeleftString,
-        itemName = itemName,
-        itemIcon = itemIcon,
-        itemQuality = itemQuality,
-        itemID = itemID
+      data.originalCreationTime = data.creationTime
+      data.originalShipmentsReady = data.shipmentsReady
 
-    --]]
-  end
-}
-
-function core:OnEvent (event, ...)
-  print(event)
-  if event == 'UNIT_PORTRAIT_UPDATE' then
-    SetPortraitTexture(self.portrait, 'player')
-  elseif event == 'PLAYER_LOGIN' then
-    if not self.initialized then
-      if IsLoggedIn() then
-        WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
-        self.data = WorldPlanData.OrderHall
-
-
-        local name, realm = UnitName('player')
-        realm  = realm or GetRealmName()
-        self.profileKey = name .. '-' .. realm
-        if not self.data[self.profileKey] then
-          self.data[self.profileKey] = {}
-        end
-        self.profile = self.data[self.profileKey]
-
-        self.profile.shipments = self.profile.shipments or {}
-        self.profile.missions = self.profile.missions or {}
-        self.profile.classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]
-
-        C_Garrison.RequestLandingPageShipmentInfo();
-        self.initialized = true
-      end
-    end
-  elseif event == 'GARRISON_LANDINGPAGE_SHIPMENTS' or event == 'GARRISON_TALENT_UPDATE' then
-    self:UpdateShipments()
-  elseif event == 'PLAYER_REGEN_ENABLED' or event == 'GARRISON_MISSION_FINISHED' or event == 'GARRISON_TALENT_COMPLETE' or event == 'GARRISON_SHIPMENT_RECEIVED' then
-    self:UpdateNotifications()
-  else
-    self:UpdateItems ()
-  end
-end
-
-function core:UpdateNotifications()
-end
-
-function core:RefreshItems(sortedItems, templateName)
-  self.blocks[templateName] = self.blocks[templateName] or {}
-  local blocks = self.blocks[templateName]
-  local template = self.templates[templateName] or {
-    parent = self.portrait,
-    point = 'TOPLEFT',
-    relativePoint ='TOPRIGHT',
-  }
-
-  local lastProfile
-  local numItems = #sortedItems
-  for i, data in ipairs(sortedItems) do
-    local block = blocks[i]
-
-    if not block then
-      block = CreateFrame('Frame', nil, self, templateName)
-      block:SetID(i)
-      template.numBlocks = (template.numBlocks or 0) + 1
-
-      if template.lastBlock then
-        block:SetPoint('TOPLEFT', template.lastBlock, 'BOTTOMLEFT', 0, 0)
-      else
-        block:SetPoint(template.point, self[template.parent] or self, template.relativePoint, 0, 0)
-      end
-      template.lastBlock = block
-      blocks[i] = block
-    end
-
-    if template.SetItemData then
-      template.SetItemData(block, data)
-    end
-
-
-    block.lastProfile = lastProfile
-    block:Refresh(data)
-    block:Show()
-    lastProfile = data.profileKey
-  end
-
-  for i = numItems + 1, template.numBlocks do
-    if blocks[i] then
-      blocks[i]:Hide()
+      data.creationTime = data.creationTime + numOrders*data.duration
+      data.shipmentsReady = data.shipmentsReady + numOrders
+      print(data.profileKey, 'shipment "'.. data.name..'" reconciling', numOrders, 'lapsed orders. -->', data.creationTime, data.shipmentsReady)
     end
   end
 end
 
-function core:Refresh()
-  if self.isStale then
-    self:SortLists()
+ShipmentsHandler.SortHandler = function(a, b)
+  if b.isComplete ~= a.isComplete then
+    return a.isComplete and true or false
   end
-  self.isStale = nil
-
-  self:RefreshItems(self.sortedMissions, 'ClassPlanBlock')
-  self:RefreshItems(self.sortedShipments, 'ClassPlanShipment')
-
-
-  local posX = self.data.posX or 0
-  local posY = self.data.posY or -24
-  local point = self.point or 'TOP'
-  local relativePoint = self.point or 'TOP'
-  self:SetPoint(point, UIParent, relativePoint, posX, posY)
-
-
+  return (a.creationTime) < (b.creationTime)
 end
 
-function core:OnUpdate()
-  if self.fadeTimer and self.fadeTimer < GetTime() then
-    self:Hide()
-  end
-end
-
-function core:OnShow()
-  if self.isStale then
-    print('updating items on show')
-    self:Refresh()
-  end
-
-end
-
-function core:SortLists()
-
-  wipe(self.sortedShipments)
-  wipe(self.sortedMissions)
-  for name, profile in pairs(self.data) do
-    local isMine = (profile == self.profile)
-    for index, data in pairs(profile.missions) do
-
-      data.classColor = profile.classColor or {r = 0.7, g = 0.7, b =0.7}
-      data.profileKey = name
-      data.isMine = (profile == self.profile)
-      tinsert(self.sortedMissions, data)
-    end
-
-    if not profile.shipments then
-      profile.shipments = {}
-      profile.shipment = nil
-    end
-
-    for index, data in pairs(profile.shipments) do
-      data.classColor = profile.classColor or {r = 0.7, g = 0.7, b =0.7}
-      data.profileKey = name
-      data.isMine = (profile == self.profile)
-      tinsert(self.sortedShipments, data)
-    end
-  end
-
-  table.sort(self.sortedMissions, function (a,b)
-    local result = false
-    if not a or not b then
-      result = true
-    else
-      if (a.isMine ~= b.isMine) and self.playerFirst then
-        result =  a.isMine
-      else
-        if (not b.missionEndTime) or (not a.missionEndTime) then
-          print('missing article', b.missionEndTime, a.missionEndTime)
-        end
-        result = ( b.missionEndTime > a.missionEndTime)
-      end
-    end
-
-    --print('cmp', (b and (b.missionEndTime .. ' ' .. tostring(b.isMine)) or '-'), '>', (a and (a.missionEndTime .. ' ' .. tostring(a.isMine)) or '-'), result, n)
-    return result
-  end)
-
-end
-
-function core:UpdateShipments()
-  print('|cFF0088FFShipments|r:', self.profileKey)
+ShipmentsHandler.GetPlayerData = function (self)
   if not self.profile then
     return
   end
@@ -272,10 +113,9 @@
   local garrisonType = LE_GARRISON_TYPE_7_0
   local buildings = C_Garrison.GetBuildings(garrisonType);
   local shipmentIndex = 0
-  print('Buildings:')
+  --print('Buildings:')
   for i = 1, #buildings do
     local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = C_Garrison.GetLandingPageShipmentInfo(buildingID);
-    print(buildings[i], name, creationTime, duration)
     tinsert(self.shipments,
       {
         shipmentType = 'Work Order',
@@ -294,11 +134,10 @@
       })
   end
 
-  print('Follower:')
+  --print('Follower:')
   local followerShipments = C_Garrison.GetFollowerShipments(garrisonType);
   for i = 1, #followerShipments do
     local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = C_Garrison.GetLandingPageShipmentInfoByContainerID(followerShipments[i]);
-    print(followerShipments[i], name, creationTime, duration)
     tinsert(self.shipments,
       {
         shipmentType = '',
@@ -314,11 +153,10 @@
       })
   end
 
-  print('Loose:')
+  --print('Loose:')
   local looseShipments = C_Garrison.GetLooseShipments(garrisonType)
   for i = 1, #looseShipments do
     local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = C_Garrison.GetLandingPageShipmentInfoByContainerID(looseShipments[i]);
-    print(looseShipments[i], name, creationTime, duration)
     tinsert(self.shipments,
       {
         shipmentType = '',
@@ -336,7 +174,7 @@
   local talentTrees = C_Garrison.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 = C_Garrison.GetCompleteTalent(garrisonType);
-  print('Talents:')
+  --print('Talents:')
   if (talentTrees) then
     for treeIndex, tree in ipairs(talentTrees) do
       for talentIndex, talent in ipairs(tree) do
@@ -361,9 +199,11 @@
   self.profile.shipments = self.profile.shipments or {}
   if #self.shipments >= 1 then
 
+
     wipe(self.profile.shipments)
-    for index, shipment in ipairs(self.shipments) do
-      tinsert(self.profile.shipments, shipment)
+    for index, data in ipairs(self.shipments) do
+      print(' ', data.shipmentType .. data.name, data.creationTime, data.duration)
+      tinsert(self.profile.shipments, data)
     end
     self.isStale = true
   end
@@ -373,34 +213,230 @@
   end
 end
 
-function core:UpdateItems ()
-  if not self.profile then
-    return
+function core:OnLoad ()
+  self:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player')
+  self:RegisterEvent('PLAYER_LOGIN')
+  self:RegisterEvent('PLAYER_ENTERING_WORLD')
+  self:RegisterEvent('ADDON_LOADED')
+  self:RegisterEvent('PLAYER_REGEN_ENABLED')
+
+  self:AddHandler('missions', MissionsHandler)
+  self:AddHandler('shipments', ShipmentsHandler)
+end
+
+function core:AddHandler(name, prototype)
+  self.prototypes[name] = setmetatable(prototype, {
+    __index = blockTemplate,
+    __tostring = function() return name end
+    })
+
+  for i, event in ipairs(prototype.events) do
+    if not self.events[event] then
+      self:RegisterEvent(event)
+      self.events[event] = {}
+      print('|cFF00FF00registering', event)
+    end
+
+    prototype.numBlocks = 0
+    if not self.events[event][name] then
+      print('adding', name, 'to', event)
+      self.events[event][name] = prototype.GetPlayerData
+    end
   end
-  self.items = C_Garrison.GetLandingPageItems(LE_GARRISON_TYPE_7_0)
+  self.sortedItems[name] = {}
+end
 
+function core:OnEvent (event, ...)
+  print(event)
+  if event == 'UNIT_PORTRAIT_UPDATE' then
+    SetPortraitTexture(self.portrait, 'player')
+  elseif event == 'PLAYER_LOGIN' then
+    if not self.initialized then
+      if IsLoggedIn() then
+        WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
+        self.data = WorldPlanData.OrderHall
 
 
+        local name, realm = UnitName('player')
+        realm  = realm or GetRealmName()
+        self.profileKey = name .. '-' .. realm
+        if not self.data[self.profileKey] then
+          self.data[self.profileKey] = {}
+        end
+        self.profile = self.data[self.profileKey]
 
-  print('|cFF0088FFLandingPageItems|r:', self.profileKey)
-  if #self.items >= 1 then
-    wipe(self.profile.missions)
-    for index, data in ipairs(self.items) do
-      print('', data.name)
-      print('  |cFF00FF00', data.timeLeft .. '|r', date("%A %I:%m %p", data.missionEndTime))
-      tinsert(self.profile.missions, data)
+        self.profile.shipments = self.profile.shipments or {}
+        self.profile.missions = self.profile.missions or {}
+        self.profile.classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]
+
+        C_Garrison.RequestLandingPageShipmentInfo();
+
+        if self.data.IsShown then
+          self:Show()
+        end
+        self.initialized = true
+      end
     end
-    print('items update pending')
-    self.isStale = true
-  end
-
-  if self:IsVisible() then
-    self:Refresh()
+  elseif self.events[event] then
+    for ptype, eventFunc in pairs(self.events[event]) do
+      print('|cFF88FF00' .. tostring(ptype) .. '|r:GetPlayerData()')
+      eventFunc(self, event)
+    end
   end
 end
 
-function block:OnComplete()
-  self.isComplete = true
+function core:UpdateNotifications()
+end
+
+function core:RefreshItems(configKey, prototype)
+  local sortedItems = self.sortedItems[configKey]
+
+  self.blocks[configKey] = self.blocks[configKey] or {}
+  local blocks = self.blocks[configKey]
+
+  local lastProfile
+  local numItems = #sortedItems
+  local totalHeight = 0
+  for i, data in ipairs(sortedItems) do
+    local block = blocks[i]
+
+    if not block then
+      block = CreateFrame('Button', nil, self, prototype.templateName)
+      block:SetID(i)
+      prototype.numBlocks = prototype.numBlocks + 1
+
+      if prototype.lastBlock then
+        block:SetPoint('TOPLEFT', prototype.lastBlock, 'BOTTOMLEFT', 0, 0)
+      else
+        block:SetPoint(prototype.point, self[prototype.parent] or self, prototype.relativePoint, 0, 0)
+      end
+      prototype.lastBlock = block
+      blocks[i] = block
+    end
+
+    totalHeight = totalHeight + block:GetHeight()
+    block.lastProfile = lastProfile
+    block:Refresh(data)
+    block:Show()
+    lastProfile = data.profileKey
+  end
+
+  for i = numItems + 1, prototype.numBlocks do
+    if blocks[i] then
+      blocks[i]:Hide()
+    end
+  end
+
+  return totalHeight
+end
+local max = math.max
+function core:Refresh()
+  if self.isStale then
+    self:SortItems()
+  end
+  self.isStale = nil
+
+  self.currentHeight = 0
+  for name, info in pairs(self.prototypes) do
+    local itemsHeight = self:RefreshItems(name, info)
+    self.currentHeight = max(itemsHeight, self.currentHeight)
+  end
+
+  if OrderHallCommandBar and OrderHallCommandBar:IsVisible() then
+    self:ClearAllPoints()
+    self:SetPoint('TOP', OrderHallCommandBar, 'BOTTOM')
+  else
+
+    local posX = self.data.posX or 0
+    local posY = self.data.posY or -24
+    local point = self.point or 'TOP'
+    local relativePoint = self.point or 'TOP'
+    self:SetPoint(point, UIParent, relativePoint, posX, posY)
+  end
+
+  self:SetHeight(self.currentHeight)
+end
+
+function core:Toggle()
+  if self:IsVisible() then
+    self:Hide()
+  else
+    self:Show()
+  end
+
+  if self.data then
+    self.data.IsShown = self:IsVisible()
+  end
+end
+
+function core:OnUpdate()
+  if self.fadeTimer and self.fadeTimer < GetTime() then
+    self:Hide()
+  end
+end
+
+function core:OnShow()
+  if self.isStale then
+    print('updating items on show')
+    self:Refresh()
+  end
+  ClassPlanButton:SetPoint('TOP', self, 'TOP', 0, 0)
+end
+function core:OnHide()
+  ClassPlanButton:SetPoint('TOP', UIParent, 'TOP', 0, 0)
+end
+
+local GI_profileKey, GI_profile, GI_isMine
+local GetItemList = function (source, dest, onGetItem)
+  if not source then
+    return
+  end
+  local numItems = 0
+  for index, data in ipairs(source) do
+    data.classColor = GI_profile.classColor or {r = 0.7, g = 0.7, b =0.7}
+    data.profileKey = GI_profileKey
+    data.isMine = GI_isMine
+    if onGetItem then
+      onGetItem(data)
+    end
+    numItems = numItems + 1
+    tinsert(dest, data)
+  end
+  return numItems
+end
+
+function core:SortItems()
+  print('|cFF0088FFSortItems()|r')
+  GI_currentTime = time()
+
+  for key, sortedItems in pairs(self.sortedItems) do
+    wipe(sortedItems)
+    local ptype = self.prototypes[key]
+    print(  'object:', ptype)
+    for name, profile in pairs(self.data.characters) do
+      GI_profileKey = name
+      GI_profile = profile
+      GI_isMine = (profile == self.profile)
+
+        local results = GetItemList(profile[key], sortedItems, ptype.OnGetItem)
+        print(' - ', name, results, 'items')
+
+    end
+
+    if ptype.SortHandler then
+      print(' sorting', key, #sortedItems)
+      table.sort(sortedItems, ptype.SortHandler)
+    end
+  end
+end
+
+
+
+function core:UpdateItems ()
+end
+
+function MissionsHandler:OnComplete()
+  self.data.isComplete = true
   self:Refresh()
 end
 
@@ -417,8 +453,8 @@
   end
 end
 
-function block:OnUpdate()
-  if self.isComplete then
+function MissionsHandler:OnUpdate()
+  if self.data.isComplete then
     return
   end
 
@@ -453,10 +489,13 @@
     (data.isComplete and 0.5 or 0.1))
 end
 
-function block:Refresh(data)
+function MissionsHandler:Refresh(data)
   data = data or self.data
   self.data = data
 
+  self.isComplete = data.isComplete
+  self.missionEndTime = data.missionEndTime
+
   local r,g,b = 1, 1, 1
   if data.isRare then
     r,g,b = 0.1, 0.4, 1
@@ -473,23 +512,21 @@
   else
     self.Icon:SetAtlas(data.typeAtlas, false)
   end
-
-  SetClassColors(self, data)
-
   if self.isComplete then
     self.TimeLeft:SetText('Complete!')
   end
+  SetClassColors(self, data)
 end
 
 
-function block:OnEnter()
+function MissionsHandler:OnEnter()
   if self.rewardInfo and self.rewardInfo.itemID then
     GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
     GameTooltip:SetItemByID(self.rewardInfo.itemID)
     GameTooltip:Show()
   end
 end
-function block:OnLeave()
+function MissionsHandler:OnLeave()
   if GameTooltip:IsOwned(self) then
     GameTooltip:Hide()
   end
@@ -497,55 +534,65 @@
 
 
 
-function shipment:Refresh(data)
+function ShipmentsHandler:Refresh(data)
   data = data or self.data
+
+  --[[
+  self.icon = data.icon
+  self.shipmentCapacity = data.shipmentCapacity
+  self.shipmentsReady = data.shipmentsReady
+  self.shipmentsTotal = data.shipmentsTotal
+  self.creationTime = data.creationTime
+  self.duration = data.duration
+  self.itemID = data.itemID
+  self.itemQuality = data.itemQuality
+      icon = texture,
+      shipmentCapacity = shipmentCapacity,
+      shipmentsReady = shipmentsReady,
+      shipmentsTotal = shipmentsTotal,
+      creationTime = creationTime,
+      duration = duration,
+      timeleftString = timeleftString,
+      itemName = itemName,
+      itemIcon = itemIcon,
+      itemQuality = itemQuality,
+      itemID = itemID
+
+  --]]
+
   self.Icon:SetTexture(data.icon)
   self.data = data
 
+
+  local isComplete = data.isComplete
+
+
+
   self.Name:SetText(data.shipmentType .. data.name)
   self.Count:SetText(data.shipmentsReady)
-
   self.Done:SetShown(data.shipmentsReady and (data.shipmentsReady >= 1))
 
-  if ( data.shipmentsReady == data.shipmentsTotal ) then
+
+  -- flag as complete
+  if ( data.shipmentsReady == data.shipmentsTotal ) and (not data.isBeingResearched) then
     self.Swipe:SetCooldownUNIX(0, 0);
     self.Done:Show();
-    if not data.isBeingResearched then
-      data.isComplete = true
-    end
+    isComplete = true
   else
     self.Swipe:SetCooldownUNIX(data.creationTime or 0 , data.duration or 0);
   end
 
-
+  data.isComplete = isComplete
 
   SetClassColors(self, data)
 end
-function shipment:UpdateShipment()
-
-  local data = self.data
-  if data.shipmentsTotal  then
-    local timeLeft = data.creationTime + data.duration - time()
-    if timeLeft < 0 then
-      local numReady = floor((1*timeLeft) / data.duration)
-      data.shipmentsReady = data.shipmentsReady + numReady
-      data.creationTime = data.creationTime + (numReady * data.duration)
-      self:Refresh()
-    end
-  end
-end
-function shipment:OnUpdate()
+function ShipmentsHandler:OnUpdate()
   local data = self.data
   if (data.shipmentsReady and data.shipmentsTotal) and (data.shipmentsReady ~= data.shipmentsTotal) then
     local timeLeft = data.creationTime + data.duration - time()
-    if timeLeft < 0 then
-      self:UpdateShipment()
-      return
-    end
-
     self.TimeLeft:SetText('Next: '.. GetTimeLeftString(timeLeft) .. ' |cFFFFFF00'..data.shipmentsTotal..' orders|r')
-
-
+  elseif data.isStale then
+    self.TimeLeft:SetText('|cFFFF0000Needs refresh|r')
   elseif data.isBeingResearched then
     self.TimeLeft:SetText(GetTimeLeftString(data.researchStartTime + data.researchDuration - time()))
   else
@@ -554,7 +601,7 @@
 
 end
 
-function shipment:OnEnter()
+function ShipmentsHandler:OnEnter()
   local data = self.data
   if ( data.shipmentsReady and data.shipmentsTotal ) then
     GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
@@ -563,12 +610,21 @@
   end
 end
 
-function shipment:OnLeave()
+function ShipmentsHandler:OnLeave()
   if GameTooltip:IsOwned(self) then
     GameTooltip:Hide()
   end
 end
 
-function shipment:OnClick(button)
-  --todo: trigger cleanup script for dead shipment data
+function ShipmentsHandler:OnClick(button)
+  if button == 'RightButton' then
+    for name, profile in pairs(ClassOrderPlan.data) do
+      for index, shipment in pairs(profile.shipments) do
+        if shipment == self.data then
+          profile.shipments[index] = nil
+          ClassOrderPlan:Refresh()
+        end
+      end
+    end
+  end
 end
\ No newline at end of file