/**********************************************
 *  AOSD 10 Submission Files                  *
 *  Pict.java :                               *
 *    All the Pict related classes and        *
 *    functions                               *
 *                                            *
 *********************************************/

import edu.neu.ccs.demeterf.Traversal;
import edu.neu.ccs.demeterf.TU;
import edu.neu.ccs.demeterf.TP;
import edu.neu.ccs.demeterf.control.Fields;
import java.io.FileOutputStream;
import java.io.PrintStream;

public abstract class Pict{
    static Pict kspiral(int k){ return khelp(k,0,360/k); }
    static Pict khelp(int k, int ang, int inc){
        if(k <= 1)return spiral(0,ang);
        return new Overlay(spiral(0,ang),
                           khelp(k-1,ang+inc,inc));
    }
    static Pict spiral(int r, int ang){
        double rads = Math.PI*ang/180.0;
        Pict curr = new Offset((int)(Math.cos(rads)*r),
                               (int)(Math.sin(rads)*r),
                               new Circle(6));
        if(r >= 800)return curr;
        return new Overlay(curr, spiral(r+3, (ang+5)%360));
    }
    static void write(String what, String file) throws Exception{
        PrintStream p = new PrintStream(new FileOutputStream(file));
        p.print(what);
        p.close();
    }

    public static void main2(String[] args){
        Pict p = new Offset(1,2,new Offset(1,2,new Offset(1,2,new Offset(1,2,new Circle(15)))));

        System.out.println(" Start: "+p.toStr());
        System.out.println(" Compr: "+new InlineParCompress(new Compress())
                           .traverse(p).toStr());
        /*System.out.println(" Compr: "+new Traversal(new Compress())
          .<Pict>traverse(p).toStr());*/
    }

    public static void main(String[] args) throws Exception{
        Pict[] pp = {
            //new Overlay(new Offset(-21,0,new Circle(20)),
            //            new Offset(21,0,new Square(40))),
            design(120)
        };

        //System.out.println(" Pict: "+new ToString().toStr(pp[0]));
        
        InlineParToSVG svg = new InlineParToSVG(new ToSVG());
        int i = 1;
        for(Pict p:pp)
            write(SVG.image(400,400,svg.traverse(p, new Ctx(200, 200))), "file-"+(i++)+".svg");
    }

    static Pict design(int r){
        Pict curr = new Circle(r);
        if(r <= 2)return curr;
        return //new Overlay(curr,
                           new Overlay(new Overlay(new Offset(r,0,design(2*r/5)),
                                                   new Offset(0,r,design(2*r/5))),
                                       new Overlay(new Offset(-r,0,design(2*r/5)),
                                                   new Offset(0,-r,design(2*r/5))));
    }


    /* Visitor */
    public static abstract class Vis<X>{
        int visit(int i){ return i; }
        abstract X visit(Circle c);
        abstract X visit(Square s);
        abstract X visit(Offset o);
        abstract X visit(Overlay o);
    }
    abstract <X> X accept(Vis<X> v);
    abstract int size();
    abstract String toStr();
    boolean isOffset(){ return false; }

    abstract int circles();
    abstract String toSVG(Ctx ctx);
    abstract Pict scale(int i);
    abstract Pict circ2sqr();
    abstract Pict flip();
    abstract Pict compress();
}

class Circle extends Pict{
    int rad;
    Circle(int r){ rad = r; }
    String toStr(){ return "Circle("+rad+")"; }
    <X> X accept(Vis<X> v){ return v.visit(this); }
    int size(){ return 1; }

    int circles(){ return 1; }
    String toSVG(Ctx ctx){ return SVG.circle(ctx.x, ctx.y, rad); }
    Pict scale(int i){ return new Circle(i*rad); }
    Pict circ2sqr(){ return new Square(rad*2); }
    Pict flip(){ return this; }
    Pict compress(){ return this; }
}

class Square extends Pict{
    int size;
    Square(int s){ size = s; }
    String toStr(){ return "Square("+size+")"; }
    <X> X accept(Vis<X> v){ return v.visit(this); }
    int size(){ return 1; }

    int circles(){ return 0; }
    String toSVG(Ctx ctx){ return SVG.square(ctx.x, ctx.y, size); }
    Pict scale(int i){ return new Square(i*size); }
    Pict circ2sqr(){ return this; }
    Pict flip(){ return this; }
    Pict compress(){ return this; }
}

class Offset extends Pict{
    int dx, dy;
    Pict inner;
    Offset(int x, int y, Pict in){ dx = x; dy = y; inner = in; }
    String toStr(){ return "Offset("+
            dx+","+ dy+","+
            inner.toStr()+")"; }
    <X> X accept(Vis<X> v){ return v.visit(this); }
    int size(){ return 1+inner.size(); }
    boolean isOffset(){ return true; }

    int circles(){ return inner.circles(); }
    String toSVG(Ctx ctx){ return inner.toSVG(ctx.move(this)); }
    Pict scale(int i){ return new Offset(dx*i,dy*i,inner.scale(i)); }
    Pict circ2sqr(){ return new Offset(dx,dy,inner.circ2sqr()); }
    Pict flip(){ return new Offset(dx,dy,inner.flip()); }
    Pict compress(){
        Pict in = inner.compress();
        if(in.isOffset()){
            Offset inn = ((Offset)in);
            return new Offset(dx+inn.dx,dy+inn.dy,inn.inner);
        }
        return new Offset(dx,dy,in);
    }
}

class Overlay extends Pict{
    Pict top, bot;
    Overlay(Pict t, Pict b){ top = t; bot = b; }
    String toStr(){ return "Overlay("+
            top.toStr()+","+
            bot.toStr()+
            ")"; }
    <X> X accept(Vis<X> v){ return v.visit(this); }
    int size(){ return 1+top.size()+bot.size(); }

    int circles(){ return top.circles()+bot.circles(); }
    String toSVG(Ctx ctx){ return top.toSVG(ctx)+bot.toSVG(ctx); }
    Pict scale(int i){ return new Overlay(top.scale(i),bot.scale(i)); }
    Pict circ2sqr(){ return new Overlay(top.circ2sqr(),bot.circ2sqr()); }
    Pict flip(){ return new Overlay(bot.flip(),top.flip()); }
    Pict compress(){ return new Overlay(bot.compress(),top.compress()); }
}

class ID extends edu.neu.ccs.demeterf.ID{
    int combine(int i){ return i; }
    String combine(String s){ return s; }
}

class CircsTU extends StaticTU<Integer>{
    public Integer combine(){ return 0; }
    public Integer fold(Integer a, Integer b){ return a+b; }
    
    Integer combine(Circle c){ return 1; }
}
class ToSVG extends ID{
    Ctx update(Offset off, Fields.any f, Ctx c)
    { return c.move(off); }

    String combine(Circle c, int r, Ctx ctx)
    { return SVG.circle(ctx.x, ctx.y, r); }
    String combine(Square s, int sz, Ctx ctx)
    { return SVG.square(ctx.x, ctx.y, sz); }
    String combine(Offset o, int dx, int dy, String shp){ return shp; }
    String combine(Overlay o, String t, String b){ return t+b; }
}
class Scale extends StaticTP{
    int fact;
    Scale(int f){ fact = f; }
    public int combine(int i){ return i*fact; }
}
class Circ2Sqr extends StaticTP{
    public Pict combine(Circle c)
    { return new Square(c.rad*2); }
}
class Flip extends StaticTP{
    public Overlay combine(Overlay o, Pict t, Pict b)
    { return new Overlay(b, t); }
}
class Compress extends StaticTP{
    public Offset combine(Offset o, int x, int y, Offset in)
    { return new Offset(x+in.dx, y+in.dy, in.inner); }
}


class ToStringVis extends Pict.Vis<String>{
    int visit(int i){ return i; }
    String visit(Circle c){ return "Circle("+c.rad+")"; }
    String visit(Square s){ return "Square("+s.size+")"; }
    String visit(Offset o)
    { return "Overlay("+o.dx+","+o.dy+","+o.inner.accept(this)+")"; }
    String visit(Overlay o)
    { return o.top.accept(this)+","+o.bot.accept(this)+")"; }
}
class CircsVis extends Pict.Vis<Integer>{
    int visit(int i){ return 0; }
    Integer visit(Circle c){ return 1; }
    Integer visit(Square s){ return 0; }
    Integer visit(Offset o)
    { return o.inner.accept(this); }
    Integer visit(Overlay o)
    { return o.top.accept(this)+o.bot.accept(this); }
}
class ToSVGVis extends Pict.Vis<String>{
    Ctx ctx;
    ToSVGVis(Ctx c){ ctx = c; }

    int visit(int i){ return i; }
    String visit(Circle c){ return SVG.circle(ctx.x,ctx.y,c.rad); }
    String visit(Square s){ return SVG.square(ctx.x,ctx.y,s.size); }
    String visit(Offset o)
    { return o.inner.accept(new ToSVGVis(ctx.move(o))); }
    String visit(Overlay o)
    { return o.top.accept(this)+o.bot.accept(this); }
}
class ScaleVis extends Pict.Vis<Pict>{
    int scale;
    ScaleVis(int s){ scale = s; }
    int visit(int i){ return i*scale; }
    Pict visit(Circle c){ return new Circle(visit(c.rad)); }
    Pict visit(Square s){ return new Square(visit(s.size)); }
    Pict visit(Offset o){
        return new Offset(visit(o.dx),visit(o.dy),o.inner.accept(this));
    }
    Pict visit(Overlay o){
        return new Overlay(o.top.accept(this),o.bot.accept(this));
    }
}
class Circ2SqrVis extends Pict.Vis<Pict>{
    Pict visit(Circle c){ return new Circle(visit(c.rad)); }
    Pict visit(Square s){ return new Square(visit(s.size)); }
    Pict visit(Offset o){
        return new Offset(visit(o.dx),visit(o.dy),o.inner.accept(this));
    }
    Pict visit(Overlay o){
        return new Overlay(o.top.accept(this),o.bot.accept(this));
    }
}
class FlipVis extends Pict.Vis<Pict>{
    Pict visit(Circle c){ return new Circle(c.rad); }
    Pict visit(Square s){ return new Square(s.size); }
    Pict visit(Offset o){
        return new Offset(visit(o.dx),visit(o.dy),o.inner.accept(this));
    }
    Pict visit(Overlay o){
        return new Overlay(o.top.accept(this),o.bot.accept(this));
    }
}
class ComprVis extends Pict.Vis<Pict>{
    Pict visit(Circle c){ return new Circle(c.rad); }
    Pict visit(Square s){ return new Square(s.size); }
    Pict visit(Offset o){
        final int dx = visit(o.dx),
                  dy = visit(o.dy);
        final Pict in = o.inner.accept(this);
        return in.accept(new Pict.Vis<Pict>(){
                Pict visit(Circle c){ return new Offset(dx,dy,c); }
                Pict visit(Square s){ return new Offset(dx,dy,s); }
                Pict visit(Overlay o){ return new Offset(dx,dy,o); }
                Pict visit(Offset o){
                    return new Offset(dx+o.dx,dy+o.dy,o.inner);
                }
            });
    }
    Pict visit(Overlay o){
        return new Overlay(o.top.accept(this),o.bot.accept(this));
    }
}

class Ctx{
    int x, y;
    Ctx(int xx, int yy){ x = xx; y = yy; }
    Ctx move(Offset o){ return move(o.dx,o.dy); }
    Ctx move(int dx, int dy){ return new Ctx(x+dx,y+dy); }
}

class SVG{
    static String head(int w, int h){
        return 
            "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n"+
            "'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>"+
            "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' "+
            "width='"+w+"' height='"+h+"'>"+
            "<rect x='1' y='1' width='"+(w-2)+"' height='"+(h-2)+"'"+
            " fill-opacity='0' stroke='black'"+
            "/>\n";
    }
    static String foot(){ return "</svg>\n"; }
    static String end(String clr){
        return " stroke='"+clr+"' stroke-opacity='0.5'"+
            " fill='white' fill-opacity='1' />\n";
    }
    static String dcolor = "black";
    static String circle(int x, int y, int r){ return circle(x,y,dcolor,r); }
    static String circle(int x, int y, String clr, int r){
        return ("  <circle cx='"+x+"' cy='"+y+
                "' r='"+r+"'"+end(clr));
    } 
    static String square(int x, int y, int s){ return square(x,y,dcolor,s); }
    static String square(int x, int y, String clr, int s){
        return rect(x,y,clr,s,s);
    }
    static String rect(int x, int y, int w, int h)
    {   return rect(x,y,dcolor,w,h); }
    static String rect(int x, int y, String clr, int w, int h){
        return ("  <rect x='"+(x-w/2)+"' y='"+(y-h/2)+
                "' width='"+w+"' height='"+h+"'"+end(clr));
    }
    static String image(int w, int h, String bdy){
        return head(w,h)+bdy+foot();
    }
}