/*
  JOB-TICKET.hpp  -  execution plan for render jobs

  Copyright (C)         Lumiera.org
    2012,               Hermann Vosseler <Ichthyostega@web.de>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#ifndef PROC_ENGINE_JOB_TICKET_H
#define PROC_ENGINE_JOB_TICKET_H

#include "proc/common.hpp"
//#include "proc/state.hpp"
#include "backend/engine/job.h"
#include "proc/engine/frame-coord.hpp"
//#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
#include "lib/hierarchy-orientation-indicator.hpp"
#include "lib/linked-elements.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/util.hpp"

#include <boost/noncopyable.hpp>
#include <stack>


namespace proc {
namespace engine {
  
//using lib::time::TimeSpan;
//using lib::time::Duration;
//using lib::time::FSecs;
//using lib::time::Time;
using backend::engine::Job;
using lib::LinkedElements;
using lib::OrientationIndicator;
using util::isnil;
//  
//class ExitNode;
  
  
  /**
   * execution plan for pulling a specific exit node.
   * Usable as blue print for generating actual render jobs.
   * Job tickets are created on demand, specialised for each segment
   * of the low-level model, and for each individual feed (corresponding
   * to a single model port). Once created, they are final for this segment,
   * stored together with the other descriptor objects (ProcNode and WiringDescriptor)
   * and finally discarded in bulk, in case that segment of the low-level model becomes
   * obsolete and is replaced by a newly built new version of this model segment.
   * 
   * Job tickets are created by a classical recursive descent call on the exit node,
   * which figures out everything to be done for generating data from this node.
   * To turn a JobTicket into an actual job, we need the additional information
   * regarding the precise frame number (=nominal time) and the channel number
   * to calculate (in case the actual feed is multichannel, which is the default).
   * This way, the JobTicket acts as <i>higher order function:</i> a function
   * generating on invocation another, specific function (= the job).
   * 
   * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
   */
  class JobTicket
    : boost::noncopyable
    {
      struct Provision
        {
          Provision* next;
          ////////////////////TODO some channel or format descriptor here
        };
      
      struct Prerequisite
        {
          Prerequisite* next;
          JobTicket* descriptor;
        };
      
      struct Prerequisites ///< per channel
        {
          Prerequisites* next;
          LinkedElements<Prerequisite> requiredJobs_;
        };
      
      
      
      LinkedElements<Provision>   channelConfig_;
      LinkedElements<Prerequisites> requirement_;
      
      
    public:
      class ExplorationState;
      friend class ExplorationState;
      
      
      JobTicket()
        {
          UNIMPLEMENTED ("job representation, planning and scheduling");
        }
      
      
      ExplorationState startExploration()                      const;
      ExplorationState discoverPrerequisites (uint channelNr)  const;
      
      Job createJobFor (FrameCoord coordinates);
      
      
      bool
      isValid()  const
        {
          if (channelConfig_.size() != requirement_.size())
            return false;
          
          UNIMPLEMENTED ("validity self check");
        }
    };
  
  
  class JobTicket::ExplorationState
    {
      typedef LinkedElements<Prerequisite>::iterator SubTicketSeq;
      typedef std::stack<SubTicketSeq> SubTicketStack;               //////////////////////////TODO use a custom container to avoid heap allocations
      
      SubTicketStack toExplore_;
      OrientationIndicator orientation_;
      
    public:
      ExplorationState() { }
      
      ExplorationState (Prerequisites& prerequisites)
        {
          if (!isnil (prerequisites.requiredJobs_))
            toExplore_.push (prerequisites.requiredJobs_.begin());
        }
      
      // using default copy operations
      
      
      bool
      empty()  const
        {
          return toExplore_.empty();
        }
      
      
      void
      markTreeLocation()
        {
          orientation_.markRefLevel (toExplore_.size());
          orientation_.resetToRef();
          ENSURE (0 == orientation_);
        }
      
      
      void
      pullNext()
        {
          if (empty())
            throw lumiera::error::Logic ("Exploration of Job prerequisites floundered. "
                                         "Attempt to iterate beyond the end of prerequisite list"
                                        ,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
          ASSERT (toExplore_.top().isValid());
          
          ++(toExplore_.top());
          while ( !toExplore_.empty()
                && toExplore_.top().empty())
            toExplore_.pop();
          
          ENSURE (empty() || toExplore_.top().isValid());
        }
      
      
      void
      push (ExplorationState subExploration)                 // note: passing deliberately by value
        {
          if (subExploration.empty()) return;
          
          pushAllPrerequisites (subExploration.toExplore_);
        }
      
      
      JobTicket*
      operator->() const
        {
          REQUIRE (!empty() && toExplore_.top().isValid());
          REQUIRE (toExplore_.top()->descriptor);
          REQUIRE (toExplore_.top()->descriptor->isValid());
          
          return toExplore_.top()->descriptor;
        }
      
      
    private:
      void
      pushAllPrerequisites (SubTicketStack& furtherPrerequisites)
        {
          REQUIRE (!isnil (furtherPrerequisites));
          
          if (1 == furtherPrerequisites.size())
            {
              this->toExplore_.push (furtherPrerequisites.top());
            }
          else 
            { // pathological case: several levels of prerequisites
              // --> push recursively to retain level ordering
              SubTicketSeq deepestLevel (furtherPrerequisites.top());
              furtherPrerequisites.pop();
              pushAllPrerequisites (furtherPrerequisites);
              this->toExplore_.push (deepestLevel);
            }
        }
    };
  
  
  
  
  inline JobTicket::ExplorationState
  JobTicket::startExploration()  const
  {
    UNIMPLEMENTED ("somehow build a self-referential pseudo-prerequisite, and seed an ExplorationState with that");
    /////////////////////TODO problem is: we need an JobTicket::Prerequisite instance, where the descriptor points to "self" (this JobTicket)
    /////////////////////TODO           : but this instance needs to reside somewhere at a safe location, since we want to embed an LinkedElements-iterator
    /////////////////////TODO           : into the ExplorationState. And obviously we do not want that instance in each JobTicket, only in the top level ones
    
    /////////////////////TODO : on second thought -- better have a top-level entry point to the evaluation of a frame
    /////////////////////TODO                        basically this inherits from Prerequisite and lives somehow in the dispatcher-table or segment
  }
  
  
  
  inline JobTicket::ExplorationState
  JobTicket::discoverPrerequisites (uint channelNr)  const
  {
    REQUIRE (channelNr < requirement_.size());
    
    return ExplorationState (requirement_[channelNr]);
  }

  
  
  
}} // namespace proc::engine
#endif
