import java.io.* ;

class Quad
{
    int nature ; // Feuille ou noeud interne `a quatre fils.
    final static int LEAF=0, NODE=1 ;

    int x, y, width ; /* Le noeud repr'esente une fen^etre de largeur
                       * width x width pixels :
                       *   du pixel x,y `a x+width-1,y+width-1. 
                       * width est suppos'e ^etre une puissance de 2.
                       */
    // Feuille :
    int color ;
    Quad (int x, int y, int l, int c) {
        nature = LEAF ;
        this.x = x ; this.y = y ;
        width = l ;
        color = c ;
    }

    // Noeud interne :
    Quad sw, se, ne, nw ;
    Quad (int x, int y, int l, Quad sw, Quad se, Quad ne, Quad nw) {
        nature = NODE ;
        this.x = x ; this.y = y ;
        width = l ;
        this.sw = sw ; this.nw = nw ;
        this.ne = ne ; this.se = se ;
    }

    static boolean monochrome(Quad q1, Quad q2, Quad q3, Quad q4) {
        return
            (q1.nature == LEAF && q2.nature == LEAF &&
             q3.nature == LEAF && q4.nature == LEAF) &&
            (q1.color == q2.color && q2.color == q3.color &&
             q3.color == q4.color) ;
    }

    static Quad newNode (Quad sw, Quad se, Quad ne, Quad nw) {
        if (monochrome (sw, se, ne, nw))
            return new Quad (nw.x, nw.y, nw.width*2, nw.color) ;
        else return new Quad (nw.x, nw.y, nw.width*2, sw, se, ne, nw) ;
    }

    static Quad rectangle (Quad q, int xa, int ya, 
                           int xb, int yb, int col) {
        // Point en bas `a droite :
        int qxm = q.x + q.width - 1, qym = q.y + q.width - 1 ;
        // Pas d'intersection :
        if (qxm < xa || q.x > xb || qym < ya || q.y > yb)
            return q ;
        // q inclus :
        if (q.x >= xa && q.y >= ya && qxm <= xb && qym <= yb)
            return new Quad (q.x, q.y, q.width, col) ;
        if (q.nature == NODE) {
            return newNode (
                rectangle (q.sw, xa, ya, xb, yb, col),
                rectangle (q.se, xa, ya, xb, yb, col),
                rectangle (q.ne, xa, ya, xb, yb, col),
                rectangle (q.nw, xa, ya, xb, yb, col)
                ) ;
        } else if (q.nature == LEAF && q.width > 1) {
            return rectangle (split (q), xa, ya, xb, yb, col) ;
        } else if (q.nature == LEAF && q.width == 1) { // un pixel
            //return new Quad (q.x, q.y, q.width, col) ;
            throw new Error ("Devrait ^etre inclus.") ;
        } else {
            throw new Error ("Nature inconnue " + q.nature) ;
        }
    }

    /* Couper en quatre une feuille. */
    static Quad split (Quad q) {
        if (q.nature != LEAF)
            throw new Error ("Spliter seulement les feuilles.") ;
        int l = q.width / 2 ;
        return new Quad (q.x, q.y, q.width,
                         new Quad (q.x, q.y+l, l, q.color),
                         new Quad (q.x+l, q.y+l, l, q.color),
                         new Quad (q.x+l, q.y, l, q.color),
                         new Quad (q.x, q.y, l, q.color)
            ) ;
    }

    static void toFenetre (Quad q, Fenetre f) {
        if (q.nature == LEAF) {
            f.rectangle (q.x, q.y, q.x+q.width, q.y+q.width, q.color) ;
        } else if (q.nature == NODE) {
            toFenetre (q.sw, f) ;
            toFenetre (q.se, f) ;
            toFenetre (q.ne, f) ;
            toFenetre (q.nw, f) ;
        } else {
            throw new Error ("Nature inconnue " + q.nature) ;
        }
    }

    /* Ecriture pr'efixe sw se ne nw :
     */
    static void write (Quad q, OutputStream out) throws IOException {
        out.write (q.nature) ;
        if (q.nature == Quad.NODE) {
            write (q.sw, out) ;
            write (q.se, out) ;
            write (q.ne, out) ; 
            write (q.nw, out) ;
        } else if (q.nature == Quad.LEAF) {
            out.write (q.color) ;
        } else
            throw new Error ("Nature inconnue " + q.nature) ;
    }

    /* Lecture pr'efixe sw se ne nw, x,y,width sont reconstruits
     */
    static Quad readQuad (int x, int y, int w,
                          InputStream inp) throws IOException {
        int nature = read8 (inp) ;
        if (nature == Quad.NODE) {
            int w2 = w/2 ;
            return newNode (readQuad (x, y+w2, w2, inp), 
                            readQuad (x+w2, y+w2, w2, inp), 
                            readQuad (x+w2, y, w2, inp), 
                            readQuad (x, y, w2, inp)
                ) ;
        } else if (nature == Quad.LEAF) {
            return new Quad (x, y, w, read8 (inp)) ;
        } else
            throw new Error ("Nature inconnue " + nature) ;
    }

    static int read8 (InputStream inp) throws IOException {
        int i = inp.read () ;
        if (i == -1)
            throw new Error ("Fin pr'ematur'ee de fichier.") ;
        return i ;
    }

}

class FenQuadS  implements Fenetre
{
    Quad r ;

    /* Bas'e sur un quad-tree repr'esentant une image
     * widthxwidth. width doit ^etre une puissance de 2.
     */
    FenQuadS (int width) {
        r = new Quad (0, 0, width, 0) ;
    }

    public void rectangle (int xa, int ya, int xb, int yb, int col) {
        r = Quad.rectangle (r, xa, ya, xb, yb, col) ;
    }

    void toFenetre (Fenetre f) {
        Quad.toFenetre (r, f) ;
    }

    void save (String file) {
        try {
            write (new FileOutputStream (file)) ;
        } catch (IOException e) {
            throw new Error (e) ;
        }
    }

    void write (OutputStream out) throws IOException {
        write16 (out, r.width) ;
        Quad.write (r, out) ;
    }

    static FenQuadS load (String file) {
        try {
            return read (new FileInputStream (file)) ;
        } catch (IOException e) {
            throw new Error (e) ;
        }
    }

    static FenQuadS read (InputStream inp) throws IOException {
        FenQuadS f = new FenQuadS (read16 (inp)) ;
        f.r = Quad.readQuad (0, 0, f.r.width, inp) ;
        return f ;
    }

    // Byte de poids fort d'abord.
    static void write16 (OutputStream out, int i) throws IOException {
        out.write ((byte)(i / 256)) ;
        out.write ((byte)(i % 256)) ;
    }

    static int read16 (InputStream inp) throws IOException {
        return Quad.read8(inp) * 256 + Quad.read8(inp) ;
    }

    public static void main (String[] args) {
        FenQuadS f = new FenQuadS (256) ;
        if (args.length == 1) {
            f = load (args[0]) ;
        } else {
            FilePrio fp = new FilePrio (1000) ;
            fp.ajouter (new Pyramide (0, 40, 50,50, 150, 150)) ;
            fp.ajouter (new Pyramide (1, 150, 60,40, 200, 140)) ;
            while (fp.nonVide ()) {
                Evenement e = fp.retirerMin () ;
                e.effectuer (f, fp) ;
            }
            f.save ("fen.quad") ;
        }
        FenEcran fen = new FenEcran (256) ;
        f.toFenetre (fen) ;
        fen.save ("fen.png") ; // Pour comparer la taille.
    }
}