
//  This file forms part of the GOBLIN C++ Class Library.
//
//  Initially written by Christian Fremuth-Paeger, May 2001
//
//  Copying, compiling, distribution and modification
//  of this source code is permitted only in accordance
//  with the GOBLIN general licence information.

/// \file   exportToTk.cpp
/// \brief  #exportToTk class implementation

#include "exportToTk.h"


exportToTk::exportToTk(const abstractMixedGraph& GG,const char* expFileName)
    throw(ERFile) : canvasBuilder(GG), expFile(expFileName, ios::out)
{
    if (!expFile)
    {
        sprintf(CT.logBuffer,"Could not open export file %s, io_state %d",
            expFileName,expFile.rdstate());
        Error(ERR_FILE,"exportToTk",CT.logBuffer);
    }

    expFile.flags(expFile.flags() | ios::right);
    expFile.setf(ios::floatfield);
    expFile.precision(5);

    canvasName = "$goblinCanvas";

    ScaleDisplayParameters(0.067,10e+6,10e+6);

    expFile << "set goblinCanvasObjects {" << endl;

    int minXNodeGrid = int(floor(minX/nodeSpacing-0.5));
    int maxXNodeGrid = int(ceil(maxX/nodeSpacing+0.5));
    int minYNodeGrid = int(floor(minY/nodeSpacing-0.5));
    int maxYNodeGrid = int(ceil((maxY+CFG.legenda)/nodeSpacing+0.5));

    expFile << "  {-1 " << ID_UPPER_LEFT << " line {"
        << int(CanvasCX(minXNodeGrid*nodeSpacing)) << " "
        << int(CanvasCY(minYNodeGrid*nodeSpacing)) << "} {} } \\" << endl;
    expFile << "  {-1 " << ID_LOWER_RIGHT << " line {"
        << int(CanvasCX(maxXNodeGrid*nodeSpacing)) << " "
        << int(CanvasCY(maxYNodeGrid*nodeSpacing)) << "} {} } \\" << endl;

    if (strcmp(CFG.wallpaper,"")!=0)
    {
//        expFile << "image create photo wallpaper -file \""
//            << CFG.wallpaper << "\"" << endl;
//        expFile << "$goblinCanvas create image 0 0 -image wallpaper"
//            << " -anchor nw" << endl;
    }
}


unsigned long exportToTk::Size() const throw()
{
    return
          sizeof(exportToTk)
        + managedObject::Allocated();
}


unsigned long exportToTk::Allocated() const throw()
{
    return 0;
}


void exportToTk::DisplayLegenda(long xm,long ym,long radius) throw()
{
    long xl = xm-radius;
    long xr = xm+radius;

    int textShift = int(arrowSize);
    if (!G.IsUndirected())
    {
        expFile << "  {-1 " << ID_GRAPH_EDGE << " line {"
            << xl << " " << ym << " " << xr-long(nodeSize) << " " << ym
            << "} {-width 2 -joinstyle bevel";

        if (arrowPosMode==ARROWS_CENTERED)
        {
            expFile << "} } \\" << endl;
            WriteArrow(NoNode,xm+long(arrowSize),ym,1,0);
            textShift = long(2*arrowSize);
        }
        else
        {
            expFile << " -arrow last -arrowshape {"
                << long(arrowSize*2) << " " << long(arrowSize*3)
                << " " << long(arrowSize*1) << "}" << "} } \\" << endl;
        }
    }
    else
    {
        expFile << "  {-1 " << ID_GRAPH_EDGE << " line {"
            << xl << " " << ym << " " << xr-long(nodeSize) << " " << ym
            << "} {-width 2 -joinstyle bevel} } \\" << endl;
    }

    if (strcmp(arcLabelFormat,"")!=0)
    {
        expFile << "  {-1 " << ID_EDGE_LABEL << " text {"
            << xm << " " << (ym-textShift) << "} {-text {";

        for (unsigned i=0;i<strlen(arcLabelFormat);i++)
        {
            if (arcLabelFormat[i]=='#' && i<strlen(arcLabelFormat)-1)
            {
                expFile << ArcLegenda(int(arcLabelFormat[i+1]-'0'));
                i++;
            }
            else
            {
                expFile << char(arcLabelFormat[i]);
            }
        }

        expFile << "} -anchor c -font \""
            << "-adobe-" << unixFontType[arcFontType] << "-"
            << int(arcFontSize) << "-0-0-0-p-0-iso8859-1"
            << "\"} } \\" << endl;
    }

    switch (nodeShapeMode)
    {
        case NODE_SHAPE_POINT:
        {
            WriteSmallNode(NoNode,xl,ym);
            WriteSmallNode(NoNode,xr,ym);
            WriteNodeLegenda(xl,ym+long(nodeSize),"u");
            WriteNodeLegenda(xr,ym+long(nodeSize),"v");

            return;
        }
        case NODE_SHAPE_BY_COLOUR:
        case NODE_SHAPE_CIRCULAR:
        {
            WriteCircularNode(NoNode,xl,ym,"#ffffff");
            WriteCircularNode(NoNode,xr,ym,"#ffffff");
            WriteNodeLegenda(xl,ym,"u");
            WriteNodeLegenda(xr,ym,"v");

            return;
        }
        case NODE_SHAPE_BOX:
        {
            WriteRectangularNode(NoNode,xl,ym,"#ffffff");
            WriteRectangularNode(NoNode,xr,ym,"#ffffff");
            WriteNodeLegenda(xl,ym,"u");
            WriteNodeLegenda(xr,ym,"v");

            return;
        }
    }
}


void exportToTk::WriteNodeLegenda(long x,long y,char* index) throw()
{
    if (strcmp(nodeLabelFormat,"")==0) return;

    expFile << "  {-1 " << ID_NODE_LABEL << " text {"
        << x << " " << y << "} {-text {";

    for (unsigned i=0;i<strlen(nodeLabelFormat);i++)
    {
        if (nodeLabelFormat[i]=='#' && i<strlen(nodeLabelFormat)-1)
        {
            expFile << NodeLegenda(index,int(nodeLabelFormat[i+1]-'0'));
            i++;
        }
        else
        {
            expFile << char(nodeLabelFormat[i]);
        }
    }

    expFile << "} -anchor c -font \""
        << "-adobe-" << unixFontType[nodeFontType] << "-"
        << int(nodeFontSize) << "-0-0-0-p-0-iso8859-1"
        << "\"} } \\" << endl;
}


void exportToTk::WritePolyLine(vector<double>& cx,vector<double>& cy,
    TLineStyle lineStyle,int width,TArrowDir displayedArrows,
    TIndex colourIndex,int depth) throw()
{
    // Write coordinates
    vector<double>::iterator px = cx.begin();
    vector<double>::iterator py = cy.begin();

    expFile << "  {" << 0 << " " << ID_GRID_LINE << " line {";

    while (px!=cx.end() && py!=cy.end())
    {
        expFile << long(*px) << " " << long(*py) << " ";
        ++px;
        ++py;
    }

    expFile << "} {";


    // Write stipple mode
    const char* stippleModes[] = {""," -dash ."," -dash -"," -dash -."};
    expFile << stippleModes[lineStyle%4];


    // Write line colour
    char* rgbColour = NULL;

    if (colourIndex==OUTLINE_COLOUR)
    {
        rgbColour = "#000000";
    }
    else if (colourIndex==GRID_COLOUR)
    {
        rgbColour = "#A0A0A0";
    }
    else if (colourIndex==PRED_COLOUR)
    {
        rgbColour = predColour;
    }
    else if (arcColourMode==ARCS_FLOATING_COLOURS)
    {
        rgbColour = SmoothColour(colourIndex - ZERO_COLOUR,maxEdgeColour,MAX_COLOUR);
    }
    else
    {
        rgbColour = FixedEdgeColour(colourIndex - ZERO_COLOUR);
    }

    expFile << " -fill " << rgbColour;


    // Write arrow display mode
    if (displayedArrows!=ARROW_NONE)
    {
        if (displayedArrows==ARROW_FORWARD)
        {
            expFile << " -arrow last";
        }
        else if (displayedArrows==ARROW_BACKWARD)
        {
            expFile << " -arrow first";
        }
        else
        {
            expFile << " -arrow both";
        }

        expFile << " -arrowshape {"
                << long(arrowSize*2) << " " << long(arrowSize*3)
                << " " << long(arrowSize*1) << "}";
    }


    // 
    if (cx.size()>2) expFile << " -joinstyle bevel";


    // Write line width
    expFile << " -width " << width << "} } \\" << endl;
}


void exportToTk::WriteArrow(TArc a,long xtop,long ytop,double dx,double dy) throw()
{
    double ox = dy;
    double oy = -dx;

    expFile << "  {" << a << " " << ID_ARROW << " polygon {"
        << long(xtop) << " "
        << long(ytop) << " "
        << long(xtop-(2*dx+ox)*arrowSize) << " "
        << long(ytop-(2*dy+oy)*arrowSize) << " "
        << long(xtop-dx*arrowSize) << " "
        << long(ytop-dy*arrowSize) << " "
        << long(xtop-(2*dx-ox)*arrowSize) << " "
        << long(ytop-(2*dy-oy)*arrowSize) << " "
        << long(xtop) << " "
        << long(ytop)
        << "} {-outline #000000 -fill #000000} } \\" << endl;
}


void exportToTk::WriteArcLabel(TArc a,long xm,long ym) throw()
{
    expFile << "  {" << a << " " << ID_EDGE_LABEL << " text {"
        << xm << " " << ym << "} {-text {";

    ComposeArcLabel(a,expFile);

    expFile << "} -anchor c -font \""
        << "-adobe-" << unixFontType[arcFontType] << "-"
        << int(arcFontSize) << "-0-0-0-p-0-iso8859-1"
        << "\"} } \\" << endl;
}


void exportToTk::DisplayNode(TNode v) throw()
{
    char* fillColour = "#ffffff";

    if (nodeColourMode==NODES_COLOUR_BY_DISTANCE)
    {
        if (G.Dist(v)==InfFloat || G.Dist(v)==-InfFloat) fillColour = inftyColour;
    }
    else if (nodeColourMode==NODES_FIXED_COLOURS)
    {
        fillColour = FixedNodeColour(G.NodeColour(v));
    }
    else if (nodeColourMode==NODES_COLOUR_BY_DEMAND)
    {
        if (G.Demand(v)>0) fillColour = FixedNodeColour(1);
        if (G.Demand(v)<0) fillColour = FixedNodeColour(2);
    }
    else if (nodeColourMode==NODES_FLOATING_COLOURS)
    {
        fillColour = SmoothColour(G.NodeColour(v),maxNodeColour,NO_COLOUR);
    }

    long x = CanvasCXOfPoint(v);
    long y = CanvasCYOfPoint(v);

    switch (nodeShapeMode)
    {
        case NODE_SHAPE_POINT:
        {
            WriteSmallNode(v,x,y);
            TNode p = G.ThreadSuccessor(v);

            if (p==NoNode)
            {
                 WriteNodeLabel(v,x+long(nodeSize),y+long(nodeSize));
            }
            else
            {
                WriteNodeLabel(v,CanvasCXOfPoint(p),CanvasCYOfPoint(p));
            }

            return;
         }
         case NODE_SHAPE_CIRCULAR:
         {
             WriteCircularNode(v,x,y,fillColour);
             WriteNodeLabel(v,x,y);

             return;
         }
         case NODE_SHAPE_BOX:
         {
             WriteRectangularNode(v,x,y,fillColour);
             WriteNodeLabel(v,x,y);

             return;
         }
         case NODE_SHAPE_BY_COLOUR:
         {
             WriteRegularNode(v,x,y,fillColour);
             WriteNodeLabel(v,x,y);

             return;
         }
    }
}


void exportToTk::WriteSmallNode(TNode v,long x,long y) throw()
{
    int radius = 5;

    if (v==NoNode) expFile << "  {-1";
    else expFile << "  {" << v;

    expFile << " " << ID_GRAPH_NODE << " oval {"
        << x-radius << " " << y-radius << " " << x+radius << " " << y+radius
        << "} {-outline #000000 -fill #000000 -width 1} } \\" << endl;
}


void exportToTk::WriteCircularNode(TNode v,long x,long y,char* fillColour) throw()
{
    double radius = nodeSize;

    if (v!=NoNode && G.ThreadSuccessor(v)!=NoNode)
    {
        TNode w = G.ThreadSuccessor(v);
        radius += sqrt(G.C(w,0)*G.C(w,0)+G.C(w,1)*G.C(w,1))*zoom;
    }

    if (v==NoNode) expFile << "  {-1";
    else expFile << "  {" << v;

    expFile << " " << ID_GRAPH_NODE << " oval {"
        << RoundToLong(x-radius) << " " << RoundToLong(y-radius) << " "
        << RoundToLong(x+radius) << " " << RoundToLong(y+radius)
        << "} {-outline #000000 -fill " << fillColour << "} } \\" << endl;
}


void exportToTk::WriteRectangularNode(TNode v,long x,long y,char* fillColour) throw()
{
    double width  = nodeSize;
    double height = nodeSize;

    if (v!=NoNode && G.ThreadSuccessor(v)!=NoNode)
    {
        TNode w = G.ThreadSuccessor(v);

        width  += G.C(w,0)*zoom;
        height += G.C(w,1)*zoom;
    }

    if (v==NoNode) expFile << "  {-1";
    else expFile << "  {" << v;

    expFile << " " << ID_GRAPH_NODE << " rectangle {"
        << RoundToLong(x-width) << " " << RoundToLong(y-height) << " "
        << RoundToLong(x+width) << " " << RoundToLong(y+height)
        << "} {-outline #000000 -fill " << fillColour << "} } \\" << endl;
}


void exportToTk::WriteRegularNode(TNode v,long x,long y,char* fillColour) throw()
{
    if (G.NodeColour(v)==0 || G.NodeColour(v)>G.N())
    {
        return WriteCircularNode(v,x,y,fillColour);
    }

    if (v==NoNode) expFile << "  {-1";
    else expFile << "  {" << v;

    unsigned nVertices = G.NodeColour(v)+3;
    double radius = nodeSize/cos(PI/nVertices);

    expFile << " " << ID_GRAPH_NODE << " poly {";

    for (unsigned i=0;i<nVertices;++i)
    {
        double xRel = radius*sin((i+0.5)*2*PI/nVertices);
        double yRel = radius*cos((i+0.5)*2*PI/nVertices);

        expFile << RoundToLong(x-xRel) << " " << RoundToLong(y+yRel) << " ";
    }

    expFile << "} {-outline #000000 -fill " << fillColour << "} } \\" << endl;
}


void exportToTk::WriteNodeLabel(TNode v,long x,long y) throw()
{
    if (strcmp(nodeLabelFormat,"")==0) return;

    expFile << "  {" << v << " " << ID_NODE_LABEL << " text {"
        << x << " " << y << "} {-text {";

    ComposeNodeLabel(v,expFile);

    expFile << "}" << " -anchor c -font \""
        << "-adobe-" << unixFontType[nodeFontType] << "-"
        << int(nodeFontSize) << "-0-0-0-p-0-iso8859-1"
        << "\"} } \\" << endl;
}


void exportToTk::DisplayArtificialNode(TNode v) throw()
{
    long x = CanvasCXOfPoint(v);
    long y = CanvasCYOfPoint(v);
    int radius = 4;

    expFile << "  {" << v << " " << ID_BEND_NODE << " rectangle {"
        << x-radius << " " << y-radius << " " << x+radius << " " << y+radius
        << "} {-outline #000000 -fill #000000} } \\" << endl;
}


exportToTk::~exportToTk() throw()
{
    for (TNode i=0;i<G.NI();++i) DisplayArtificialNode(G.N()+i);

    expFile << "}" << endl;
    expFile.close();
}
