/* === S Y N F I G ========================================================= */
/*!	\file centerlineadjustments.cpp
**	\brief centerlineadjustments File
**
**	$Id$
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**
**	This package 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 package 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.
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */


#include "polygonizerclasses.h"



/* === U S I N G =========================================================== */

using namespace std;
using namespace etl;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */
VectorizerCoreGlobals *globals;
std::vector<unsigned int> contourFamilyOfOrganized;
JointSequenceGraph *currJSGraph;
ContourFamily *currContourFamily;


/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

/* === E N T R Y P O I N T ================================================= */
//--------------------------------------
//      Skeleton re-organization
//--------------------------------------

// EXPLANATION:  The raw skeleton data obtained from StraightSkeletonizer
// class need to be grouped in joints and sequences before proceeding with
// conversion in quadratics - which works on single sequences.

// NOTE: Due to maxHeight, we have to assume that a single SkeletonGraph can
// hold
// more connected graphs at once.

// a) Isolate graph-like part of skeleton
// b) Retrieve remaining single sequences.

typedef std::map<UINT, UINT, std::less<UINT>> uintMap;

// void organizeGraphs(SkeletonList* skeleton)
void studio::organizeGraphs(SkeletonList *skeleton, VectorizerCoreGlobals &g) 
{
  globals = &g;

  SkeletonList::iterator currGraphPtr;
  Sequence currSequence;
  uintMap jointsMap;
  UINT i, j;

  UINT counter = 0;  // We also count current graph number, to associate
  // organized graphs to their contour family

  contourFamilyOfOrganized.clear();

  for (currGraphPtr = skeleton->begin(); currGraphPtr != skeleton->end();++currGraphPtr) 
  {
    SkeletonGraph &currGraph   = **currGraphPtr;
    currSequence.m_graphHolder = &currGraph;

    // Separate single Points - can happen only when a single node gets stored
    // in a SkeletonGraph.
    if (currGraph.getNodesCount() == 1) 
    {
      globals->singlePoints.push_back(*currGraph.getNode(0));
      ++counter;
      continue;
    }

    // Discriminate between graphs, two-endpoint single sequences, and circular
    // ones
    bool has1DegreePoint = 0;
    for (i = 0; i < currGraph.getNodesCount(); ++i)
    {
        if (currGraph.getNode(i).degree() != 2)
        {
            if (currGraph.getNode(i).degree() == 1)
                has1DegreePoint = 1;
            else
                goto _graph;
        }
    }
      
    if (has1DegreePoint)
      goto _two_endpoint;
    else
      goto _circulars;

  _two_endpoint : {
    // Find head
    for (i = 0; currGraph.getNode(i).degree() != 1; ++i);

    currSequence.m_head     = i;
    currSequence.m_headLink = 0;

    // Find tail
    for (++i; i < currGraph.getNodesCount() && currGraph.getNode(i).degree() == 2;  ++i);

    currSequence.m_tail     = i;
    currSequence.m_tailLink = 0;

    globals->singleSequences.push_back(currSequence);

    ++counter;
    continue;
  }

  _graph : {
    // Organize Graph-like part
    globals->organizedGraphs.push_back(JointSequenceGraph());
    JointSequenceGraph &JSGraph = globals->organizedGraphs.back();
    contourFamilyOfOrganized.push_back(counter);

    jointsMap.clear();

    // Gather all sequence extremities
    for (i = 0; i < currGraph.getNodesCount(); ++i) 
    {
      if (currGraph.getNode(i).degree() != 2) 
      {
        j = JSGraph.newNode(i);
        // Using a map to keep one-to-one relation between j and i
        jointsMap.insert(uintMap::value_type(i, j));
      }
    }

    // Extract Sequences
    for (i = 0; i < JSGraph.getNodesCount(); ++i) 
    {
      UINT joint = *JSGraph.getNode(i);
      for (j = 0; j < currGraph.getNode(joint).getLinksCount(); ++j) 
      {
        currSequence.m_head     = joint;
        currSequence.m_headLink = j;

        // Seek tail
        UINT oldNode  = joint,
             thisNode = currGraph.getNode(joint).getLink(j).getNext();
        while (currGraph.getNode(thisNode).degree() == 2) 
        {
          currGraph.node(thisNode).setAttribute(
              ORGANIZEGRAPHS_SIGN);  // Sign thisNode as part of a JSGraph
          currSequence.advance(oldNode, thisNode);
        }

        currSequence.m_tail = thisNode;
        currSequence.m_tailLink = currGraph.getNode(thisNode).linkOfNode(oldNode);

        JSGraph.newLink(i, jointsMap.find(thisNode)->second, currSequence);
      }
    }
  }

  // NOTE: The following may seem uncommon - you must observe that, *WHEN
  // maxThickness<INF*,
  //      more isolated sequence groups may arise in the SAME SkeletonGraph; so,
  //      an organized
  //      graph may contain different unconnected basic graph-structures.
  //      Further, remaining circular sequences may still exist. Therefore:

  // Proceed with remaining circulars extraction

  _circulars : {
    // Extract all circular sequences
    // Find a sequence point
    for (i = 0; i < currGraph.getNodesCount(); ++i)
    {
      if (!currGraph.getNode(i).hasAttribute(ORGANIZEGRAPHS_SIGN) &&
          currGraph.getNode(i).degree() == 2) {
        unsigned int curr = i, currLink = 0;
        currSequence.next(curr, currLink);

        for (; curr != i; currSequence.next(curr, currLink))
          currGraph.node(curr).setAttribute(ORGANIZEGRAPHS_SIGN);

        // Add sequence
        currSequence.m_head = currSequence.m_tail = i;
        currSequence.m_headLink                   = 0;
        currSequence.m_tailLink                   = 1;

        globals->singleSequences.push_back(currSequence);
      }
    }
  }
  }
}

//==========================================================================



