/* Graph6Loader.java
 * =========================================================================
 * This file is part of the GrInvIn project - http://www.grinvin.org
 * 
 * Copyright (C) 2005-2008 Universiteit Gent
 * 
 * This program 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 program 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.
 * 
 * A copy of the GNU General Public License can be found in the file
 * LICENSE.txt provided with the source distribution of this program (see
 * the META-INF directory in the source jar). This license can also be
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 * 
 * If you did not receive a copy of the GNU General Public License along
 * with this program, contact the lead developer, or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.grinvin.io.graphs;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ResourceBundle;

import org.grinvin.graphs.DefaultGraphBundle;
import org.grinvin.graphs.Embedding;
import org.grinvin.graphs.Graph;
import org.grinvin.graphs.GraphBundle;
import org.grinvin.graphs.Vertex;
import org.grinvin.list.graphs.GraphList;
import org.grinvin.list.graphs.GraphListElementManager;
import org.grinvin.util.InternationalizedProperties;

/**
 * Several static methods to load graph6 (.g6) files in GrInvIn
 */
public class Graph6Loader {
    
    private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.grinvin.io.resources");
    
    /**
     * Reads the entire file and stores all the graphs in a GraphInvariantListModel
     */
    public static void readFile(GraphList list, File file) throws FileNotFoundException, IOException {
        BufferedReader buf = new BufferedReader(new FileReader(file));
        String graph = buf.readLine();
        int i = 1;
        while(graph != null && graph.length()!=0){
            GraphBundle bundle = new DefaultGraphBundle();
            InternationalizedProperties iprops = new InternationalizedProperties();
            iprops.setProperty("graph.name", BUNDLE.getString("graph.name") + " " + (i++));
            bundle.setProperties(iprops);
            readGraph(bundle, new StringReader(graph));
            list.add(GraphListElementManager.getInstance().createGraphListElement(bundle));
            graph = buf.readLine();
        }
    }
    
    /**
     * Only reads the first graph in the file and stores it in a GraphBundle.
     */
    public static void readSingleGraph(GraphBundle bundle, File file) throws FileNotFoundException, IOException{
        Reader reader = new FileReader(file);
        InternationalizedProperties iprops = new InternationalizedProperties();
        iprops.setProperty("graph.name", file.getName());
        bundle.setProperties(iprops);
        readGraph(bundle, reader);
    }
    
    //read a graph and store it in the bundle
    private static void readGraph(GraphBundle bundle, Reader reader) throws FileNotFoundException, IOException{
        //determine order of the graph
        int c = reader.read();
        int order;
        if (c=='>') {
            //header, so skip it
            while(c!='<')
                c = reader.read();
            reader.read(); //header ends with <<
            c=reader.read();
        }
        
        if(c==-1)
            throw new RuntimeException("EOF"); //TODO: do something else
        else if (c==126) {
            int c2 = reader.read();
            if(c2==126)
                //order n was greater or equal to 258047 --> N(n)=126 126 R(x)
                order = (reader.read()-63)*(1<<30) + (reader.read()-63)*(1<<24) +(reader.read()-63)*(1<<18) +
                        (reader.read()-63)*(1<<12) + (reader.read()-63)*(1<<6) + (reader.read()-63);
            else
                //order n was greater or equal to 63, but smaller than 258048 --> N(n)=126 R(x)
                order = (c2-63)*(1<<12) + (reader.read()-63)*(1<<6) + (reader.read()-63);
        } else
            //order n was between 0 and 63 --> N(n)=n+63
            order = c-63;
        
        
        Graph graph = bundle.createGraph();
        Embedding embedding = bundle.createEmbedding();
        embedding.setDimension(2);
        Vertex[] vertices = new Vertex[order];
        //no embedding comes with graph6 file, so we just place the vertices on a circle
        for(int i = 0; i < order; i++) {
            double angle = 2 * i * Math.PI / order;
            double[] coords = new double[2];
            coords[0] = Math.cos(angle);
            coords[1] = Math.sin(angle);
            vertices[i] = graph.addNewVertex();
            embedding.setCoordinates(vertices[i], coords);
        }
        boolean[] sixBits = new boolean[6];
        getSixBits(reader.read()-63,sixBits);
        int k = 0;
        for(int i = 1; i<order; i++){
            for(int j = 0; j<i; j++){
                if(sixBits[k++])
                    graph.addNewEdge(vertices[i], vertices[j]);
                if(k>5){
                    getSixBits(reader.read()-63,sixBits);
                    k=0;
                }
            }
        }
        reader.close();
    }
    
    //transform an integer to a six bit representation
    private static void getSixBits(int c, boolean[] sixBits){
        for(int i = 5; i>=0; i--){
            sixBits[i]=(c%2 == 1);
            c = c/2;
        }
    }
}
