/*****
* runlabel.in
*
* Runtime functions for label operations.
*
*****/

pen      => primPen()
 pair     => primPair()
 path     => primPath()
 picture* => primPicture()
 transform => primTransform()
 realarray* => realArray()
 stringarray* => stringArray()
 penarray* => penArray()
 patharray* => pathArray()
 patharray2* => pathArray2()

#include "picture.h"
#include "drawlabel.h"
#include "locate.h"

using namespace camp;
using namespace vm;
using namespace settings;

typedef array realarray;
typedef array stringarray;
typedef array penarray;
typedef array patharray;
typedef array patharray2;

using types::realArray;
using types::stringArray;
using types::penArray;
using types::pathArray;
using types::pathArray2;

void cannotread(const string& s)
{
 ostringstream buf;
 buf << "Cannot read from " << s;
 error(buf);
}

void cannotwrite(const string& s)
{
 ostringstream buf;
 buf << "Cannot write to " << s;
 error(buf);
}

pair readpair(stringstream& s, double hscale=1.0, double vscale=1.0)
{
 double x,y;
 s >> y;
 s >> x;
 return pair(hscale*x,vscale*y);
}

string ASYx="/ASYx {( ) print ASYX sub 12 string cvs print} bind def";
string ASYy="/ASYy {( ) print ASYY sub 12 string cvs print} bind def";
string pathforall="{(M) print ASYy ASYx} {(L) print ASYy ASYx} {(C) print ASYy ASYx ASYy ASYx ASYy ASYx} {(c) print} pathforall";
string currentpoint="print currentpoint ASYy ASYx ";
string ASYinit="/ASYX currentpoint pop def /ASYY currentpoint exch pop def ";
string ASY1="ASY1 {"+ASYinit+"/ASY1 false def} if ";

void endpath(std::ostream& ps)
{
 ps << ASY1 << pathforall << " (M) " << currentpoint
    << "currentpoint newpath moveto} bind def" << endl;
}

void fillpath(std::ostream& ps)
{
 ps << "/fill {closepath ";
 endpath(ps);
}

void showpath(std::ostream& ps)
{
 ps << ASYx << newl
    << ASYy << newl
    << "/ASY1 true def" << newl
    << "/stroke {strokepath ";
 endpath(ps);
 fillpath(ps);
}

array *readpath(const string& psname, bool keep,
               double hscale=1.0, double vsign=1.0)
{
 double vscale=vsign*hscale;
 array *PP=new array(0);

 char *oldPath=NULL;
 string dir=stripFile(outname());
 if(!dir.empty()) {
   oldPath=getPath();
   setPath(dir.c_str());
 }

 mem::vector<string> cmd;
 cmd.push_back(getSetting<string>("gs"));
 cmd.push_back("-q");
 cmd.push_back("-dBATCH");
 cmd.push_back("-P");
 if(safe) cmd.push_back("-dSAFER");
#ifdef __MSDOS__
 const string null="NUL";
#else
 const string null="/dev/null";
#endif
 string psdriver=getSetting<string>("psdriver");
 cmd.push_back("-sDEVICE="+psdriver);
 cmd.push_back("-sOutputFile="+null);
 cmd.push_back(stripDir(psname));
 iopipestream gs(cmd,"gs","Ghostscript");
 while(gs.running()) {
   stringstream buf;
   string s=gs.readline();
   if(s.empty()) break;
   gs << newl;

// Workaround broken stringstream container in libc++.
#ifdef _LIBCPP_VERSION
   for(string::iterator i=s.begin(); i != s.end(); ++i) {
     if(isalpha(*i) && *i != 'e') {buf << " ";}
     buf << *i;
   }
#else
   buf << s;
#endif

   if(verbose > 2) cout << endl;

   mem::vector<solvedKnot> nodes;
   solvedKnot node;
   bool active=false;

   array *P=new array(0);
   PP->push(P);

   while(!buf.eof()) {
     char c='>';
     buf >> c;
     if(c == '>') break;

     switch(c) {
       case 'M':
       {
         node.pre=node.point=readpair(buf,hscale,vscale);
         node.straight=false;
         break;
       }
       case 'L':
       {
         pair point=readpair(buf,hscale,vscale);
         pair delta=(point-node.point)*third;
         node.post=node.point+delta;
         node.straight=true;
         nodes.push_back(node);
         active=true;
         node.pre=point-delta;
         node.point=point;
         break;
       }
       case 'C':
       {
         pair point=readpair(buf,hscale,vscale);
         pair pre=readpair(buf,hscale,vscale);
         node.post=readpair(buf,hscale,vscale);
         node.straight=false;
         nodes.push_back(node);
         active=true;
         node.pre=pre;
         node.point=point;
         break;
       }
       case 'c':
       {
         if(active) {
           if(node.point == nodes[0].point)
             nodes[0].pre=node.pre;
           else {
             pair delta=(nodes[0].point-node.point)*third;
             node.post=node.point+delta;
             nodes[0].pre=nodes[0].point-delta;
             node.straight=true;
             nodes.push_back(node);
           }
           P->push(path(nodes,nodes.size(),true)); // Discard noncyclic paths
           nodes.clear();
         }
         active=false;
         node.straight=false;
         break;
       }
     }
   }
 }

 if(oldPath != NULL)
   setPath(oldPath);

 if(!keep)
   unlink(psname.c_str());
 return PP;
}

// Autogenerated routines:


void label(picture *f, string *s, string *size, transform t, pair position,
          pair align, pen p)
{
 f->append(new drawLabel(*s,*size,t,position,align,p));
}

bool labels(picture *f)
{
 return f->havelabels();
}

realarray *texsize(string *s, pen p=CURRENTPEN)
{
 texinit();
 processDataStruct &pd=processData();

 string texengine=getSetting<string>("tex");
 setpen(pd.tex,texengine,p);

 double width,height,depth;
 texbounds(width,height,depth,pd.tex,*s);

 array *t=new array(3);
 (*t)[0]=width;
 (*t)[1]=height;
 (*t)[2]=depth;
 return t;
}

patharray2 *_texpath(stringarray *s, penarray *p)
{
 size_t n=checkArrays(s,p);
 if(n == 0) return new array(0);

 string prefix=cleanpath(outname());
 string psname=auxname(prefix,"ps");
 string texname=auxname(prefix,"tex");
 string dviname=auxname(prefix,"dvi");
 bbox b;
 string texengine=getSetting<string>("tex");
 bool xe=settings::xe(texengine) || settings::lua(texengine) ||
   settings::context(texengine);
 texfile tex(texname,b,true);
 tex.miniprologue();

 for(size_t i=0; i < n; ++i) {
   tex.setfont(read<pen>(p,i));
   if(i != 0) {
     if(texengine == "context")
       tex.verbatimline("}\\page\\hbox{%");
     else if(texengine == "luatex" || texengine == "tex" ||
             texengine == "pdftex")
       tex.verbatimline("\\eject");
     else
       tex.verbatimline("\\newpage");
   }
   if(!xe) {
     tex.verbatimline("\\special{ps:");
     tex.verbatimline(ASYx);
     tex.verbatimline(ASYy);
     tex.verbatimline("/ASY1 true def");
     tex.verbatimline("/show {"+ASY1+
                      "currentpoint newpath moveto false charpath "+pathforall+
                      "} bind def");
     tex.verbatimline("/V {"+ASY1+"Ry neg Rx 4 copy 4 2 roll 2 copy 6 2 roll 2 copy (M) print ASYy ASYx (L) print ASYy add ASYx (L) print add ASYy add ASYx (L) print add ASYy ASYx (c) print} bind def}");
   }
   tex.verbatimline(read<string>(s,i)+"\\ %");
 }

 tex.epilogue(true);
 tex.close();

 int status=opentex(texname,prefix,!xe);

 string pdfname,psname2;
 bool keep=getSetting<bool>("keep");

 if(!status) {
   if(xe) {
     string psdriver=getSetting<string>("psdriver");
     pdfname=auxname(prefix,"pdf");
     psname2=auxname(prefix+"_","ps");
     if(!fs::exists(pdfname)) return new array(n);
     std::ofstream ps(psname.c_str(),std::ios::binary);
     if(!ps) cannotwrite(psname);

     showpath(ps);

     mem::vector<string> cmd;
     cmd.push_back(getSetting<string>("gs"));
     cmd.push_back("-q");
     cmd.push_back("-dNoOutputFonts");
     cmd.push_back("-dNOPAUSE");
     cmd.push_back("-dBATCH");
     if(safe) cmd.push_back("-dSAFER");
     cmd.push_back("-sDEVICE="+psdriver);
     cmd.push_back("-sOutputFile="+psname2);
     cmd.push_back(pdfname);
     status=System(cmd,0,true,"gs");

     std::ifstream in(psname2.c_str());
     ps << in.rdbuf();
     ps.close();
   } else {
     if(!fs::exists(dviname)) return new array(n);
     mem::vector<string> dcmd;
     dcmd.push_back(getSetting<string>("dvips"));
     dcmd.push_back("-R");
     dcmd.push_back("-Pdownload35");
     dcmd.push_back("-D600");
     push_split(dcmd,getSetting<string>("dvipsOptions"));
     if(verbose <= 2) dcmd.push_back("-q");
     dcmd.push_back("-o"+psname);
     dcmd.push_back(dviname);
     status=System(dcmd,0,true,"dvips");
   }
 } else
   error("texpath failed");

 if(!keep) { // Delete temporary files.
   unlink(texname.c_str());
   if(!getSetting<bool>("keepaux"))
     unlink(auxname(prefix,"aux").c_str());
   unlink(auxname(prefix,"log").c_str());
   unlink(xe ? pdfname.c_str() : dviname.c_str());
   if(settings::context(texengine)) {
     unlink(auxname(prefix,"top").c_str());
     unlink(auxname(prefix,"tua").c_str());
     unlink(auxname(prefix,"tuc").c_str());
     unlink(auxname(prefix,"tui").c_str());
   }
 }
 return xe ? readpath(psname,keep,0.1) : readpath(psname,keep,0.12,-1.0);
}

patharray2 *textpath(stringarray *s, penarray *p)
{
 size_t n=checkArrays(s,p);
 if(n == 0) return new array(0);

 string prefix=cleanpath(outname());
 string outputname=auxname(prefix,getSetting<string>("textoutformat"));

 string textname=auxname(prefix,getSetting<string>("textextension"));
 std::ofstream text(textname.c_str());

 if(!text) cannotwrite(textname);

 for(size_t i=0; i < n; ++i) {
   text << getSetting<string>("textprologue") << newl
        << read<pen>(p,i).Font() << newl
        << read<string>(s,i) << newl
        << getSetting<string>("textepilogue") << endl;
 }
 text.close();

 string psname=auxname(prefix,"ps");
 std::ofstream ps(psname.c_str());
 if(!ps) cannotwrite(psname);

 showpath(ps);

 mem::vector<string> cmd;
 cmd.push_back(getSetting<string>("textcommand"));
 push_split(cmd,getSetting<string>("textcommandOptions"));
 cmd.push_back(textname);
 iopipestream typesetter(cmd);
 typesetter.block(true,false);

 mem::vector<string> cmd2;
 cmd2.push_back(getSetting<string>("gs"));
 cmd2.push_back("-q");
 cmd2.push_back("-dNoOutputFonts");
 cmd2.push_back("-dNOPAUSE");
 cmd2.push_back("-dBATCH");
 cmd2.push_back("-P");
 if(safe) cmd2.push_back("-dSAFER");
 cmd2.push_back("-sDEVICE="+getSetting<string>("psdriver"));
 cmd2.push_back("-sOutputFile=-");
 cmd2.push_back("-");
 iopipestream gs(cmd2,"gs","Ghostscript");
 gs.block(false,false);

 // TODO: Simplify by connecting the pipes directly.
 for(;;) {
   string out;
   if(typesetter.isopen()) {
     typesetter >> out;
     if(!out.empty()) gs << out;
     else if(!typesetter.running()) {
       typesetter.pipeclose();
       gs.eof();
     }
   }
   string out2;
   gs >> out2;
   if(out2.empty() && !gs.running()) break;
   ps << out2;
 }
 ps.close();

 if(verbose > 2) cout << endl;

 bool keep=getSetting<bool>("keep");
 if(!keep) // Delete temporary files.
   unlink(textname.c_str());
 return readpath(psname,keep,0.1);
}

patharray *_strokepath(path g, pen p=CURRENTPEN)
{
 array *P=new array(0);
 if(g.size() == 0) return P;

 string prefix=cleanpath(outname());
 string psname=auxname(prefix,"ps");
 bbox b;
 psfile ps(psname,false);
 ps.prologue(b);
 ps.verbatimline(ASYx);
 ps.verbatimline(ASYy);
 ps.verbatimline("/stroke {"+ASYinit+pathforall+"} bind def");
 ps.resetpen();
 ps.setpen(p);
 ps.write(g);
 ps.strokepath();
 ps.stroke(p);
 ps.verbatimline("(M) "+currentpoint);
 ps.epilogue();
 ps.close();
 array *a=readpath(psname,getSetting<bool>("keep"));
 return a->size() > 0 ? read<array *>(a,0) : a;
}