import java.util.*;
import java.io.*;

/** 
 * Rappresenta una mappa, ha un vettore di snodi, uno di segmenti di
 * vie e uno di infrastrutture.
 * Ha anche funzioni di lettura da file e scrittura su file.
 */
public class Mappa
{
  /** Contiene tutti i segmenti di via. */
  protected Vector segmenti = new Vector(100,10);
  /** Contiene tutti i punti di snodo. */
  protected Vector snodi = new Vector(100,10);
  /** Contiene tutte le infrastrutture. */
  protected Vector infrastrutture = new Vector(100,10);
  
  /** Numero di segmenti di via presenti. */
  protected int quantiSegmenti = 0;
  /** Numero di punti di snodo presenti. */
  protected int quantiSnodi = 0;
  /** Numero di infrastrutture presenti. */
  protected int quanteInfrastrutture = 0;

  /** Estremo del dominio. */ protected double minX;
  /** Estremo del dominio. */ protected double minY;
  /** Estremo del dominio. */ protected double maxX;
  /** Estremo del dominio. */ protected double maxY;

  /** X minima del dominio */  public double xMinima() { return minX; }
  /** Y minima del dominio */  public double yMinima() { return minY; }
  /** X massima del dominio */ public double xMassima() { return maxX; }
  /** Y massima del dominio */ public double yMassima() { return maxY; }
      
  /** Ritorna il numero di punti di snodo. */
  public int numeroSnodi() {  return quantiSnodi;  }
  
  /** Ritorna il numero di segmenti di via. */
  public int numeroSegmenti() {  return quantiSegmenti;  }
  
  /** Ritorna il numero di infrastrutture. */
  public int numeroInfrastrutture() {  return quanteInfrastrutture;  }

  /** Ritorna il punto di snodo i-esimo, null se non esiste. */
  public Snodo loSnodo(int i)
  {
    if ((i<0)||(i>quantiSnodi)) return null;
    return (Snodo)snodi.get(i);  
  }

  /** Ritorna il segmento di via i-esimo, null se non esiste. */
  public SegmentoVia ilSegmento(int i)
  {
    if ((i<0)||(i>quantiSegmenti)) return null;
    return (SegmentoVia)segmenti.get(i);  
  }

  /** Ritorna l'indice occupato dal segmento nella mappa, ovvero l'indice
      i tale che s==ilSegmento(i), ritorna -1 se il segmento non c'e'. */
  public int indiceSegmento(SegmentoVia s)
  {  return segmenti.indexOf((Object)s);  }

  /** Ritorna l'infrastruttura i-esima, null se non esiste. */
  public Infrastruttura laInfrastruttura(int i)
  {
    if ((i<0)||(i>quanteInfrastrutture)) return null;
    return (Infrastruttura)infrastrutture.get(i); 
  }
  
  /** Aggiunge uno snodo alla mappa. */
  public boolean inserisci(Snodo p)
  {
    snodi.addElement((Object)p);
    quantiSnodi++;
    return true;
  }
  
  /** Aggiunge un segmento di via alla mappa. */
  public boolean inserisci(SegmentoVia s)
  {
    segmenti.addElement((Object)s);
    quantiSegmenti++;
    return true;
  }
 
  /** Aggiunge un'infrastruttura alla mappa. */
  public boolean inserisci(Infrastruttura i)
  {
    infrastrutture.addElement((Object)i);
    quanteInfrastrutture++;
    return true;
  }  

  /** Per formato file. */ protected static final String NP = "NumPunti:";
  /** Per formato file. */ protected static final String NS = "NumSegmenti:";
  /** Per formato file. */ protected static final String NI = "NumInfra:";
  /** Per formato file. */ protected static final String PT = "Punto:";
  /** Per formato file. */ protected static final String SV = "Via:";
  /** Per formato file. */ protected static final String IS = "Infra:";
  
  /** Scrive la mappa. */
  public void scrivi(PrintStream file)
  {
    int i;
    /* intestazione */
    file.println(NP + " " + quantiSnodi);
    file.println(NS + " " + quantiSegmenti);
    file.println(NI + " " + quanteInfrastrutture);
    /* punti con sole coordinate */
    for (i=0; i<quantiSnodi; i++)
    {
      Snodo p = loSnodo(i);
      file.println(PT + " " + p.x + " " + p.y);
// INIZIO DEBUG
      /* segmenti incidenti nei punti */
/*
      file.print("Num: " + p.numIncidenti + " incidenti:");
      for (int j=0; j<p.numIncidenti; j++)
      {
        Object o =  p.incidenti.elementAt(j);
        file.print(" " + segmenti.indexOf(o));
      }
      file.println();
*/
// FINE DEBUG      
    }
    /* segmenti */    
    for (i=0; i<quantiSegmenti; i++)
    {
      SegmentoVia s = ilSegmento(i);
      file.print(SV + " " + MyReader.INIZIO + s.nome + MyReader.FINE);
      file.print(" " + snodi.indexOf(s.p1) + " " + snodi.indexOf(s.p2));
      file.print("   " + s.spessore);
      file.print("   " + s.pari1 + " " + s.pari2);
      file.print("   " + s.dispari1 + " " + s.dispari2);
      file.println();
// INIZIO DEBUG
      /* infrastrutture nei segmenti 
      file.print("   ha " + s.numeroInfrastrutture() + " infrastrutture: ");
      for (int j=0; j<s.numeroInfrastrutture(); j++)
      {
        Infrastruttura iss = s.infrastruttura(j);
        file.print(" /  " + iss.ilNome());
      }
      file.println();*/
// FINE DEBUG      
    }
    /* infrastrutture */
    for (i=0; i<quanteInfrastrutture; i++)
    {
      Infrastruttura t = laInfrastruttura(i);
      file.print(IS + " " + MyReader.INIZIO + t.ilNome() + MyReader.FINE);
      file.print(" " + MyReader.INIZIO + Infrastruttura.nomeDelTipo(t.ilTipo()) + MyReader.FINE);
      file.print(" " + MyReader.INIZIO + t.laVia().ilNome() + MyReader.FINE);
      file.println(" " + t.ilNumero());
    }
  }

  /** Legge questa mappa, sollevando eccezione se ci sono problemi;
      per poter essere letta con successo, la mappa deve essere vuota. */
  public void provaLeggere(MyReader rd) throws Exception
  {
System.out.println("Leggo intestaz");
    if ( (quantiSnodi!=0) || (quantiSegmenti!=0) )
      throw new Exception("Mappa non vuota");
    int numP = 0;
    int numS = 0;
    int numI = 0;
    int i;
System.out.println("Leggo intestaz");
    /* intestazione */
    if (!rd.findKeyword(NP))
       throw new Exception("Missing keyword " + NP);
    numP = rd.readInteger();
    if (!rd.findKeyword(NS))
       throw new Exception("Missing keyword " + NS);
    numS = rd.readInteger();  
    if (!rd.findKeyword(NI))
       throw new Exception("Missing keyword " + NI);
    numI = rd.readInteger();
System.out.println("Leggo punti");
    /* punti con sole coordinate */
    for (i=0; i<numP; i++)
    {
      if (!rd.findKeyword(PT))
        throw new Exception("Missing keyword " + PT);
      double xx = rd.readDouble();
      double yy = rd.readDouble();
      Snodo p = new Snodo(xx,yy);
      if (i==0)
      {  minX = maxX = xx; minY = maxY = yy;  }
      else
      {
        if (xx<minX) minX = xx; else if (xx>maxX) maxX = xx;
       if (yy<minY) minY = yy; else if (yy>maxY) maxY = yy;
      }
      inserisci(p);
    }
//System.out.println("Letti " + quantiSnodi + " punti"); 
//System.out.println("Leggo segmenti");
    /* segmenti */    
    for (i=0; i<numS; i++)
    {
      int aux1, aux2;
      if (!rd.findKeyword(SV))
        throw new Exception("Missing keyword " + SV);
      String sNome = rd.readTitle();
      /* estremi */
      aux1 = rd.readInteger();
      aux2 = rd.readInteger();
      Snodo pInizio = loSnodo(aux1);
      Snodo pFine = loSnodo(aux2);
      SegmentoVia s = new SegmentoVia(pInizio, pFine);
      inserisci(s);
      s.cambiaNome(sNome);
      /* spessore */
      double spess = rd.readDouble();
      if (spess<0) 
        throw new Exception("spessore negativo");
      s.spessore = spess;
      /* intervallo numeri pari */
      aux1 = rd.readInteger();
      aux2 = rd.readInteger();
      if ((aux1 % 2)!=0) 
        throw new Exception("Even number expected - " + aux1);
      if ((aux2 % 2)!=0) 
        throw new Exception("Even number expected - " + aux2);
      if (aux2<aux1)
        throw new Exception("Error in interval specification");
      s.pari1 = aux1;
      s.pari2 = aux2;
      /* intervallo numeri dispari */
      aux1 = rd.readInteger();
      aux2 = rd.readInteger();
      if ((aux1 % 2)!=1) 
        throw new Exception("Odd number expected - " + aux1);
      if ((aux2 % 2)!=1) 
        throw new Exception("Odd number expected - " + aux2);
      if (aux2<aux1)
        throw new Exception("Error in interval specification");
      s.dispari1 = aux1;
      s.dispari2 = aux2;
    }
//System.out.println("Letti " + quantiSegmenti + " segmenti");
//System.out.println("Leggo infrastrutture");
    /* infrastrutture */    
    for (i=0; i<numI; i++)
    {
      String aux1;
      int aux2;
      if (!rd.findKeyword(IS))
         throw new Exception("Missing keyword " + IS);
      /* nome dell'infrastruttura */
      String sNome = rd.readTitle();
      /* tipo */
      aux1 = rd.readTitle();
      aux2 = Infrastruttura.tipoDelNome(aux1);
      if (aux2==0)
         throw new Exception("tipo infrastruttura sconosciuto " + aux1);
      Infrastruttura infra = new Infrastruttura(aux2, sNome);
      /* via */
      aux1 =  rd.readTitle();
      /* numero civico */
      aux2 = rd.readInteger();     
      int j;
      /* cerca nella mappa il segmento di via corrispondente */
      for (j=0; j<quantiSegmenti; j++)
      {
/*
System.out.print("  provo segmento " + j + " se ha nome / numero " + aux1  
+ " / " + aux2 + " : " );
if (ilSegmento(j).corrisponde(aux1,aux2))
System.out.println(" --- si"); else System.out.println(" --- no");
*/
        if (ilSegmento(j).corrisponde(aux1,aux2))
           infra.cambiaIndirizzo(ilSegmento(j),aux2);
      }
      if (infra.laVia()==null)
         throw new Exception("indirizzo non valido " + aux1 + " " + aux2);
      inserisci(infra);
    }
//System.out.println("Lette " + quanteInfrastrutture + " infrastrutture");

  }

  /** Legge questa mappa, non solleva eccezioni ma se ci sono stati
      problemi ritorna false, se la mappa e' stata letta ritorna true;
      per poter essere letta con successo, la mappa deve essere vuota. */
  public boolean leggi(MyReader rd)
  {
    try {  provaLeggere(rd); return true;  }
    catch(Exception excp) {  return false;  }
  }
   
}
