package IO;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import Model.LayeredGraphEdge;
import Model.LayeredGraphNode;
import Model.LayeredNode;

/**
 * reads a {@link LayeredGraphNode} from a JSON file
 * @author kolja
 *
 */
public class Reader {

	private String fileName;
	
	public Reader( String inputFileName )
	{
		fileName = inputFileName;
	}
	
	/**
	 * read the JSON file and return its contents as a {@link LayeredGraphNode}
	 * @return the read {@link LayeredGraphNode}
	 */
	public LayeredGraphNode readInputGraph()
	{
		String file = "";
		try {
			BufferedReader r = new BufferedReader( new FileReader( fileName ) );
			String tmp = null;
			while( (tmp = r.readLine()) != null )
				file += tmp;
			r.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			JSONObject json = new JSONObject( file );
			return parseNode( json, null );
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private LayeredGraphNode parseNode( JSONObject node, LayeredGraphNode parent ) throws JSONException
	{
		LayeredGraphNode newNode = new LayeredNode( null, null );
		if( parent != null )
			newNode = parent.createNode( null );
        if( node.has( "name" ) )
        {
            if( parent != null && parent.findNodeByName( node.getString( "name" ) ) != null )
                throw new JSONException( "Node " + node.getString( "name" ) + " is already known" );
            newNode.setName( node.getString( "name" ) );
        }
        if( node.has( "width" ) )
            newNode.setWidth( node.getInt( "width" ), null );
        if( node.has( "height" ) )
            newNode.setHeight( node.getInt( "height" ), null );
		if( node.has( "layers" ) )
		{
			JSONArray layers = node.getJSONArray( "layers" );
			for( int i = 0; i < layers.length(); i++ )
			{
				for( LayeredGraphNode n : parseLayer( layers.getJSONArray( i ), newNode ) )
					n.setLayer( i );
			}
		}
		if( node.has( "edges" ) )
		{
			JSONArray edges = node.getJSONArray( "edges" );
			for( int i = 0; i < edges.length(); i++ )
			{
				JSONObject edge = edges.getJSONObject( i );
				newNode.addEdge( parseEdge( edge, newNode ) );
			}
		}
		return newNode;
	}
	
	private LayeredGraphEdge parseEdge( JSONObject edge, LayeredGraphNode parent ) throws JSONException
	{
		if( !edge.has( "source" ) || !edge.has( "target" ) )
			throw new JSONException( edge + " is no valid Layered Graph Edge." );
		if( parent.findNodeByName( edge.getString( "source" ) ) == null )
            throw new JSONException( edge + " is no valid Layered Graph Edge." );
        if( parent.findNodeByName( edge.getString( "target" ) ) == null )
            throw new JSONException( edge + " is no valid Layered Graph Edge." );
		return parent.createSimpleEdge( null, parent.findNodeByName( edge.getString( "source" ) ), parent.findNodeByName( edge.getString( "target" ) ) );
	}
	
	private ArrayList<LayeredGraphNode> parseLayer( JSONArray layer, LayeredGraphNode parent ) throws JSONException
	{
		ArrayList<LayeredGraphNode> nodes = new ArrayList<>();
		for( int i = 0; i < layer.length(); i++ )
		{
			JSONObject node = layer.getJSONObject( i );
			nodes.add( parseNode( node, parent ) );
		}
		return nodes;
	}
}
