package btools.mapcreator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;

/**
 * Parser for OSM data
 *
 * @author ab
 */
public class OsmParser extends MapCreatorBase
{
  private BufferedReader _br;

  private NodeListener nListener;
  private WayListener wListener;
  private RelationListener rListener;

  public void readMap( File mapFile,
                        NodeListener nListener,
                        WayListener wListener,
                        RelationListener rListener ) throws Exception
  {
 
    this.nListener = nListener;
    this.wListener = wListener;
    this.rListener = rListener;

    if ( mapFile == null )
    {
      _br = new BufferedReader(new InputStreamReader(System.in));
    }
    else
    {
      if ( mapFile.getName().endsWith( ".gz" ) )
      {
        _br = new BufferedReader(new InputStreamReader( new GZIPInputStream( new FileInputStream( mapFile ) ) ) );
      }
      else
      {
        _br = new BufferedReader(new InputStreamReader( new FileInputStream( mapFile ) ) );
      }
    }

    for(;;)
    {
      String line = _br.readLine();
      if ( line == null ) break;

      if ( checkNode( line ) ) continue;
      if ( checkWay( line ) ) continue;
      if ( checkRelation( line ) ) continue;
      if ( checkChangeset( line ) ) continue;
    }

    if ( mapFile != null )
    {
      _br.close();
    }
  }


  private boolean checkNode( String line ) throws Exception
  {
    int idx0 = line.indexOf( "<node id=\"" );
    if ( idx0 < 0 ) return false;
    idx0 += 10;
    int idx1 = line.indexOf( '"', idx0 );

    long nodeId = Long.parseLong( line.substring( idx0, idx1 ) );

    int idx2 = line.indexOf( " lat=\"" );
    if ( idx2 < 0 ) return false;
    idx2 += 6;
    int idx3 = line.indexOf( '"', idx2 );
    double lat = Double.parseDouble( line.substring( idx2, idx3 ) );
    int idx4 = line.indexOf( " lon=\"" );
    if ( idx4 < 0 ) return false;
    idx4 += 6;
    int idx5 = line.indexOf( '"', idx4 );
    double lon = Double.parseDouble( line.substring( idx4, idx5 ) );

    NodeData n = new NodeData( nodeId, lon, lat );

    if ( !line.endsWith( "/>" ) )
    {
      // read additional tags
      for(;;)
      {
        String l2 = _br.readLine();
        if ( l2 == null ) return false;

        int i2;
        if ( (i2 = l2.indexOf( "<tag k=\"" )) >= 0 )
        { // property-tag
          i2 += 8;
          int ri2 =  l2.indexOf( '"', i2 );
          String key = l2.substring( i2, ri2 );
          i2 = l2.indexOf( " v=\"", ri2 );
          if ( i2 >= 0 )
          {
            i2 += 4;
            int ri3 = l2.indexOf( '"', i2 );
            String value = l2.substring( i2, ri3 );

            n.putTag( key, value );
          }
        }
        else if ( l2.indexOf( "</node>" ) >= 0 )
        { // end-tag
           break;
        }
      }
    }
    nListener.nextNode( n );
    return true;
  }


  private boolean checkWay( String line ) throws Exception
  {
    int idx0 = line.indexOf( "<way id=\"" );
    if ( idx0 < 0 ) return false;

    idx0 += 9;
    int idx1 = line.indexOf( '"', idx0 );
    long id = Long.parseLong( line.substring( idx0, idx1 ) );

    WayData w = new WayData( id );

    // read the nodes
    for(;;)
    {
      String l2 = _br.readLine();
      if ( l2 == null ) return false;

      int i2;
      if ( (i2 = l2.indexOf( "<nd ref=\"" )) >= 0 )
      { // node reference
        i2 += 9;
        int ri2 =  l2.indexOf( '"', i2 );
        long nid = Long.parseLong( l2.substring( i2, ri2 ) );
        w.nodes.add( nid );
      }
      else if ( (i2 = l2.indexOf( "<tag k=\"" )) >= 0 )
      { // property-tag
        i2 += 8;
        int ri2 =  l2.indexOf( '"', i2 );
        String key = l2.substring( i2, ri2 );
        i2 = l2.indexOf( " v=\"", ri2 );
        if ( i2 >= 0 )
        {
          i2 += 4;
          int ri3 = l2.indexOf( '"', i2 );
          String value = l2.substring( i2, ri3 );
          w.putTag( key, value );
        }
      }
      else if ( l2.indexOf( "</way>" ) >= 0 )
      { // end-tag
        break;
      }
    }
    wListener.nextWay( w );
    return true;
  }

  private boolean checkChangeset( String line ) throws Exception
  {
    int idx0 = line.indexOf( "<changeset id=\"" );
    if ( idx0 < 0 ) return false;

    if ( !line.endsWith( "/>" ) )
    {
        int loopcheck = 0;
        for(;;)
        {
            String l2 = _br.readLine();
            if ( l2.indexOf("</changeset>") >= 0 || ++loopcheck > 10000 ) break;
        }
    }
    return true;
  }

  private boolean checkRelation( String line ) throws Exception
  {
    int idx0 = line.indexOf( "<relation id=\"" );
    if ( idx0 < 0 ) return false;

    idx0 += 14;
    int idx1 = line.indexOf( '"', idx0 );
    long rid = Long.parseLong( line.substring( idx0, idx1 ) );

    RelationData r = new RelationData( rid );

    // read the nodes
    for(;;)
    {
      String l2 = _br.readLine();
      if ( l2 == null ) return false;

      int i2;
      if ( (i2 = l2.indexOf( "<member type=\"way\" ref=\"" )) >= 0 )
      { // node reference
        i2 += 24;
        int ri2 =  l2.indexOf( '"', i2 );
        long wid = Long.parseLong( l2.substring( i2, ri2 ) );
        r.ways.add( wid );
      }
      else if ( (i2 = l2.indexOf( "<tag k=\"" )) >= 0 )
      { // property-tag
        i2 += 8;
        int ri2 =  l2.indexOf( '"', i2 );
        String key = l2.substring( i2, ri2 );
        i2 = l2.indexOf( " v=\"", ri2 );
        if ( i2 >= 0 )
        {
          i2 += 4;
          int ri3 = l2.indexOf( '"', i2 );
          String value = l2.substring( i2, ri3 );
          r.putTag( key, value );
        }
      }
      else if ( l2.indexOf( "</relation>" ) >= 0 )
      { // end-tag
        break;
      }
    }
    rListener.nextRelation( r );
    return true;
  }

}
