
import java.net.*;
import java.io.*;
import java.util.*;

/** This class manages the process of downloading a set
 *  of sound resources from the internet. The class makes
 *  the SoundDownloads available to other classes incrementally
 *  as they become available. This allows an application to
 *  start using the sound files which are available without 
 *  waiting for the whole download process to complete.
 *  @see: DataFile, SoundDownload, Record etc 
 */
public class DownloadManager extends Object
{
  //--------------------------------------------
  /** A set of data records which represent information 
   *  about language sound files. This may be only a 
   *  subset of those contained in the DataFile */
  private ArrayList records; 
  //--------------------------------------------
  /** represents those downloads which were successful */
  private ArrayList goodDownloads;
  //--------------------------------------------
  /** represents those downloads which were not successful */
  private ArrayList badDownloads;
  //--------------------------------------------
  /** This set contains those SoundDownloads which have been
   *  downloaded since another class has called the 
   *  getFreshDownloads method */
  private ArrayList freshDownloads;
  //--------------------------------------------
  /** This flag gets set when a new language sound file is 
   *  successfully downloaded */
  private boolean hasFreshDownloads;
  //--------------------------------------------
  private Date startTime;
  //--------------------------------------------
  private Date finishTime;
  //--------------------------------------------
  private static String NEWLINE = System.getProperty("line.separator");


  //--------------------------------------------
  /** a constructor which doesnt do much */
  public DownloadManager()
  {
    this.hasFreshDownloads = false;
    this.records = new ArrayList();
    this.goodDownloads = new ArrayList();
    this.badDownloads = new ArrayList();
    this.freshDownloads = new ArrayList();
  } //-- constr: ()

  //--------------------------------------------
  public DownloadManager(Record[] dataRecords)
  {
    this();
    for (int ii = 0; ii < dataRecords.length; ii++)
    {
      this.records.add(dataRecords[ii]);
    }
  } //-- constr: (records)

  //--------------------------------------------
  public DownloadManager(DataFile dataFile)
  {
    this();
    ArrayList recordSet = dataFile.getRecords();

    Iterator ii = recordSet.iterator();
    while (ii.hasNext())
    {
      this.records.add(ii.next());
    }
  } //-- constr: (datafile)

  //--------------------------------------------
  /** adds a task to be managed */
  public void addTask(Record rTask)
  {
    this.records.add(rTask);
  }

  //--------------------------------------------
  /** informs how long the manager has been working, milliseconds */
  public long getDuration()
  {
    
    if (this.startTime == null)
     { return 0; }

    if (this.finishTime == null)
    {
      Date dNow = new Date();
      return dNow.getTime() - this.startTime.getTime();
    }

    return this.finishTime.getTime() - this.startTime.getTime();

  } //-- method getDuration

  //--------------------------------------------
  public int completedDownloads()
  {
    return this.goodDownloads.size() + this.badDownloads.size();
  }

  //--------------------------------------------
  /** tell the number of successful tasks */
  public int countGoodDownloads()
   { return this.goodDownloads.size(); }
  
  //--------------------------------------------
  /** tell the number of unsuccessful tasks */
  public int countBadDownloads()
   { return this.badDownloads.size(); }    
  


  //--------------------------------------------
  /** allows another class to know if this class has
   *  download new sound files and if it is worth
   *  calling the getFreshDownloads method */

  public boolean hasFreshDownloads()
   { return this.hasFreshDownloads;  }

  //--------------------------------------------
  /** retrieves the successful downloads */
  public ArrayList getGoodDownloads()
   { return this.goodDownloads; }    
  
  //--------------------------------------------
  /** retrieves the unsuccessful downloads */
  public ArrayList getBadDownloads()
   { return this.badDownloads; }    

  //--------------------------------------------
  /**
   * returns those downloads which have been retrieved since
   * the last call to this method.   */ 
  public ArrayList getFreshDownloads() 
  {
    ArrayList aaReturn = new ArrayList();
    Iterator ii = this.freshDownloads.iterator();
    while (ii.hasNext())
    {
      aaReturn.add((SoundDownload)ii.next());
    }

    this.freshDownloads.clear();
    this.hasFreshDownloads = false;
    return aaReturn; 
  }


  //--------------------------------------------
  /** begin the process of downloading the sounds */ 
  public void startDownloading()
  {
    this.startTime = new Date();

    SoundDownload sdCurrent;
    Iterator ii = this.records.iterator();

    while (ii.hasNext())
    {
      sdCurrent = new SoundDownload((Record)ii.next());
      sdCurrent.download();
      if (sdCurrent.wasSuccessful())
      {
        this.goodDownloads.add(sdCurrent);
        this.freshDownloads.add(sdCurrent);
        this.hasFreshDownloads = true; 
      }
      else
      {
        this.badDownloads.add(sdCurrent);
      } //-- if, else       
    } //-- while

    this.finishTime = new Date();

  } //-- method: startDownloading()
  
  //--------------------------------------------
  public String print()
  { 
    StringBuffer sbReturn = new StringBuffer();
    return this.toString();
  }


  //--------------------------------------------
  /** displays a concise summary. */
  public String toString() 
  {
    StringBuffer sbReturn = new StringBuffer();
    SoundDownload sdCurrentSoundDownload;

    sbReturn.append("Sound Download Manager: ");

    sbReturn.append("(");
    sbReturn.append(this.records.size());
    sbReturn.append(" loads, ");
    sbReturn.append("successful:");
    sbReturn.append(this.countGoodDownloads());
    sbReturn.append(", failed:");
    sbReturn.append(this.countBadDownloads());
    sbReturn.append(", time taken:");
    sbReturn.append((float)this.getDuration()/1000);
    sbReturn.append(" secs");
    sbReturn.append(")");


    if (this.startTime != null)
    {
      sbReturn.append(" started at:");
      sbReturn.append(this.startTime);
    }

    if (this.finishTime != null)
    {
      sbReturn.append(" -finished-");
    }
    else
    {
      sbReturn.append(" -not started-");
    }

    return sbReturn.toString();
  } //-- method: toString


  //--------------------------------------------
  /**
   * prints information about all the attempts to download resources.
   *
   * @see "the toString() and printFullReport() methods"
   * @return a string which is suitable for displaying somewhere
   */
  public String printStatistics() 
  {
    StringBuffer sbReturn = new StringBuffer();

    SoundDownload sdCurrentSoundDownload;


    sbReturn.append("-Sound Download Manager-");
    sbReturn.append(NEWLINE);
    sbReturn.append("successful loads :");
    sbReturn.append(this.countGoodDownloads());
    sbReturn.append(NEWLINE);
    sbReturn.append("failed loads     :");
    sbReturn.append(this.countBadDownloads());
    sbReturn.append(NEWLINE);

    if (this.startTime == null)
    {
      sbReturn.append("start time  :");
      sbReturn.append(" -not started-");
    }
    else
    {
      sbReturn.append("work time   :");
      sbReturn.append((float)this.getDuration()/1000);
      sbReturn.append(" secs");
      sbReturn.append(NEWLINE);
      sbReturn.append("start time  :");
      sbReturn.append(this.startTime);
    }

    sbReturn.append(NEWLINE);

    if (this.finishTime == null)
    {
      sbReturn.append("finish time :");
      sbReturn.append(" -not finished-");
    }
    else
    {
      sbReturn.append("finish time :");
      sbReturn.append(this.finishTime);
    }

    sbReturn.append(NEWLINE);
    sbReturn.append(NEWLINE);

    Iterator ii;

    ii = this.goodDownloads.iterator();
    while (ii.hasNext()) 
    {
      sbReturn.append("* ");
      sdCurrentSoundDownload = (SoundDownload)ii.next();
      sbReturn.append(sdCurrentSoundDownload.toString());
      sbReturn.append(NEWLINE);
      sbReturn.append(NEWLINE);
    } //-- while

    //sbReturn.append("--failed loads--");
    //sbReturn.append(NEWLINE);

    ii = this.badDownloads.iterator();
    while (ii.hasNext()) 
    {
      sbReturn.append("* ");
      sdCurrentSoundDownload = (SoundDownload)ii.next();
      sbReturn.append(sdCurrentSoundDownload.toString());
      sbReturn.append(NEWLINE);
      sbReturn.append(NEWLINE);
    } //-- while

    return sbReturn.toString();

  } //-- method: printStatistics


  //--------------------------------------------
  /** allows the display of information about all the attempts
   *  to download resources from the internet. */
  public String printFullReport() 
  {
    StringBuffer sbReturn = new StringBuffer();

    SoundDownload sdCurrentSoundDownload;


    sbReturn.append("**Sound Download Manager**");
    sbReturn.append(NEWLINE);
    sbReturn.append("Total Successful Tasks :");
    sbReturn.append(this.countGoodDownloads());
    sbReturn.append(NEWLINE);
    sbReturn.append("Total Failed Tasks :");
    sbReturn.append(this.countBadDownloads());
    sbReturn.append(NEWLINE);
    sbReturn.append(NEWLINE);

    sbReturn.append("--successful loads--");
    sbReturn.append(NEWLINE);
    Iterator ii;

    ii = this.goodDownloads.iterator();
    while (ii.hasNext()) 
    {
      sbReturn.append(NEWLINE);
      sdCurrentSoundDownload = (SoundDownload)ii.next();
      sbReturn.append(sdCurrentSoundDownload.printStatistics());
    } //-- while

    sbReturn.append("--failed loads--");
    sbReturn.append(NEWLINE);

    ii = this.badDownloads.iterator();
    while (ii.hasNext()) 
    {
      sbReturn.append(NEWLINE);
      sdCurrentSoundDownload = (SoundDownload)ii.next();
      sbReturn.append(sdCurrentSoundDownload.printStatistics());
    } //-- while

    return sbReturn.toString();
  } //-- method: printFullReport


  //--------------------------------------------
  public static void main(String[] args) throws Exception
  {
    StringBuffer sbUserMessage = new StringBuffer("");
    //sbUserMessage.append("usage: java DownloadManager [datafile]");
    sbUserMessage.append("usage: java DownloadManager .");


    if (args.length == 0)
    {
      System.out.println(sbUserMessage);
      System.exit(-1);
    }


    StringBuffer sOutput = new StringBuffer();
    DataFile dfTest;

    if (!args[0].equals("."))
    {
      dfTest = new DataFile(args[0]);

      /*
      try
      {
        File fDataFile = new File(args[0]);  
        dfTest = new DataFile();
      }
      catch (Exception e)
      {
        sOutput.append("There was a problem with the [datafile] argument you passed");
        sOutput.append(args[0]);
        sOutput.append("The error message generated was");
        sOutput.append(e);
        sOutput.append(sbUserMessage);
        sOutput.append("");

        System.exit(-1);
      }
      */
    }
    else
    {


       Record rTest1 = new Record();
       Record rTest2 = new Record();
       Record rTest3 = new Record();
       rTest1.setSoundUrl(
       "http://java.sun.com/docs/books/tutorial/sound/example-1dot2/bottle-open.wav");
       rTest2.setSoundUrl(
       "http://java.sun.com/docs/books/tutorial/sound/example-1dot2/spacemusic.au");
       rTest3.setSoundUrl(
       "http://java.sun.com/docs/books/tutorial/example-1dot2/bottle-open.wav");

       dfTest = new DataFile();
       dfTest.addRecord(rTest1);
       dfTest.addRecord(rTest2);
       dfTest.addRecord(rTest3);

     } //--if

     DownloadManager dmTest = new DownloadManager(dfTest);

     dmTest.startDownloading();
     ArrayList soundDownloads = dmTest.getGoodDownloads();
     //System.out.println("s=" + soundDownloads.size());
     Iterator ii = soundDownloads.iterator();

     /*
     while (ii.hasNext()) 
     {
       ((SoundDownload)ii.next()).getSoundObject().play();
       
     } //-- while
     */


     //-- make a simple command loop 
     //--
     BufferedReader brUserInput = new BufferedReader(
        new InputStreamReader(System.in));

     String sCommand = "";
     SoundDownload sdCurrent = (SoundDownload)ii.next();
     sbUserMessage.setLength(0);

     sbUserMessage.append("Commands;");
     sbUserMessage.append(NEWLINE);
     sbUserMessage.append(" p - play the current sound");
     sbUserMessage.append(NEWLINE);
     sbUserMessage.append(" n - play the next sound");
     sbUserMessage.append(NEWLINE);
     sbUserMessage.append(" s - show download information for the sound");
     sbUserMessage.append(NEWLINE);
     sbUserMessage.append(" d - show the data record");
     sbUserMessage.append(NEWLINE);
     sbUserMessage.append(" q - quit");
     sbUserMessage.append(NEWLINE);
     System.out.println(sbUserMessage);

     //----------------------------------
     //-- the command loop
     //--
     while (!sCommand.equals("q"))
     {
       if (sCommand.equals("p"))
       {
         sdCurrent.getSoundObject().play();               
       }

       if (sCommand.equals("n"))
       {
         if (!ii.hasNext())
         {
           ii = soundDownloads.iterator();
         }

         sdCurrent = (SoundDownload)ii.next();
         sdCurrent.getSoundObject().play();
       }

       if (sCommand.equals("s"))
       {
         System.out.println(sdCurrent);               
       }

       if (sCommand.equals("d"))
       {
         System.out.println(sdCurrent.getRecord());               
       }

       System.out.print(">");
       sCommand = brUserInput.readLine();
     } //-- while


     System.out.println(dmTest.printStatistics());
     System.out.println(dmTest.toString());
      
  
  } //-- main()
} //-- DownloadManager class
