/*
 * Problem.java
 *
 * Created on 12. říjen 2000, 11:26
 * Documented 8.7.2001
 */

package timetable.data;

import java.util.Vector;
import timetable.util.*;
/** Realizace celého rozvrhovacího problému.
 *
 * @author  Tomáš Müller
 * @version 1.0
 */
public class Problem extends java.lang.Object implements java.io.Serializable {
    /** Globální konfigurace*/
    public Config config = null;
    /** Počet slotů - hodnota načtená z konfigurace, pro urychlení*/
    private int nrSlots = 0;

    /** Množina všech aktivit */
    public ActivityGroup activities = new ActivityGroup();
    /** Množina všech závislostí */
    public ActivityDependenceGroup dependences = new ActivityDependenceGroup();        
    /** Pole množin všech zdrojů. Jednotlivé množiny musí být navzájem disjunktní. 
     * Jednotlivé typy zdrojů lze takto oddělit. Například vyučující, učebny a vyučované předměty */
    public ResourceGroup resources[] = null;
    
    /** Nastavení problému z jiného.
     * @param jiný problém
     */
    public void setProblem(Problem problem) {
        this.config = problem.config;
        this.nrSlots = problem.nrSlots;
        this.activities = problem.activities;
        this.dependences = problem.dependences;
        this.resources = problem.resources;
    }
    
    /** Konstruktor.
     * @param config globální konfigurace
     */
    public Problem(Config config) throws TimetableException {
        this.config=config;
        nrSlots=config.getInt(Config.NR_SLOTS);
    }
    
    /** Kopírovací konstruktor.
     * @param problem jiný problém
     */
    public Problem(Problem problem) throws TimetableException {
        this(problem.config);
        this.activities=problem.activities;
        this.dependences=problem.dependences;
        this.resources=problem.resources;
    }
    
    /** Zjištění konzistence a odstranění případných nekonzistentních aktivit z rozvrhu.
     */
    public void checkConsistency() throws TimetableException {
        for (int i=0;i<activities.size();i++) {
            Activity a = activities.get(i);
            if (!a.isConsistent(dependences)) a.remove(null);
        }
    }

    /** Byl rozvrh změněn?
     * @return true, pokud ano
     */
    public boolean isChanged() {
        boolean changed = false;
        for (int i=0;i<resources.length && !changed;i++)
            changed=changed||resources[i].isChanged();
        return activities.isChanged() || /*dependences.isChanged() || */changed;
    }

    /** Vrátí seznam nenaplánovaných aktivit.
     * @return seznam nenaplánovaných aktivit
     */
    public ActivityGroup unscheduledActivities() {
        ActivityGroup g = new ActivityGroup();
        for (int i=0;i<activities.size();i++) {
            if (!activities.get(i).isScheduled()) g.add(activities.get(i));
        }
        return g;
    }
    
    /** Odebere všechny naplánované aktivity z rozvrhu.
     */
    public void reset() {
        for (int i=0;i<activities.size();i++) 
            activities.get(i).remove(null);
    }
    
    /** Vynuluje čítače počtu odebrání aktivit*/
    public void clearCounts() {
        for (int i=0;i<activities.size();i++) 
            activities.get(i).nrRemoves=0;
    }
    
    /** Uloží současný rozvrh do předchozího (activity.start -> activity.lastStart atd). */
    public void saveState() {
        for (int i=0;i<activities.size();i++) {
            if (activities.get(i).selectedResources==null) 
                activities.get(i).lastSelectedResources = null;
            else 
                activities.get(i).lastSelectedResources = (ActivityResources) activities.get(i).selectedResources.clone(); 
            activities.get(i).lastStart = activities.get(i).start;
        };
    }
    
    /** Obnoví poslední rozvrh*/
    public void restoreLastState() {
        reset();
        for (int i=0;i<activities.size();i++) {
            activities.get(i).start = activities.get(i).lastStart;
            activities.get(i).schedule(activities.get(i).start, (activities.get(i).lastSelectedResources==null?null:(ActivityResources) activities.get(i).lastSelectedResources.clone()));
        };
    }
    
    /** Uloží současný rozvrh do nejlepšího (activity.start -> activity.lastStart atd). */
    public void saveBestState() {
        for (int i=0;i<activities.size();i++) {
            if (activities.get(i).selectedResources==null) 
                activities.get(i).bestSelectedResources = null;
            else 
                activities.get(i).bestSelectedResources = (ActivityResources) activities.get(i).selectedResources.clone(); 
            activities.get(i).bestStart = activities.get(i).start;
        };
    }
    
    /** Obnoví nejlepší rozvh*/
    public void restoreBestState() {
        reset();
        for (int i=0;i<activities.size();i++) {
            activities.get(i).start = activities.get(i).bestStart;
            activities.get(i).schedule(activities.get(i).start, (activities.get(i).bestSelectedResources==null?null:(ActivityResources) activities.get(i).bestSelectedResources.clone()));
        };
    }

    /** Vrátí podřetězec řetězce dané délky. Pokud je řetězec příliž krátky doplní ho mezerami (na konec). 
     * @param s řetězec
     * @param length délka podřetězce
     * @return podřetězec dané délky
     */
    static private String subStr(String s, int length) {
        while (s.length()<length) s=new String(s+" ");
        if (s.length()>length) s=s.substring(0,length);
        return s;
    }
    
    /** Tisk rozvrhu: výpis prázdné řádky
     * @param osw výstupní stream
     */
    private void writeLine(java.io.OutputStreamWriter osw ) throws java.io.IOException {
        osw.write("----+");
        for (int i=0;i<nrSlots;i++)
            osw.write("----+");
        osw.write("\n");
    }

    /** Tisk rozvrhu: výpis hlavičky řádky
     * @param osw výstupní stream
     */
    protected void writeHead(java.io.OutputStreamWriter osw ) throws java.io.IOException {
        osw.write("    |");
        for (int i=0;i<nrSlots;i++)
            osw.write(subStr(""+(1+i),4)+"|");
        osw.write("\n");
        writeLine(osw);
    }

    /** Tisk rozvrhu do souboru
     * @param file soubor
     * @param allResources skupina zdrojů, pro které se má rozvrh vytiskoun
     * @param order pořadí
     * @param size velikost
     */
    public void print (String file, ResourceGroup allResources, int order, int size) throws TimetableException {
        try {
            java.io.FileOutputStream fos = new java.io.FileOutputStream(file);
            java.io.OutputStreamWriter osw = new java.io.OutputStreamWriter(fos);
            try {
                print(osw,allResources, order,size);
            } finally { osw.flush(); osw.close(); fos.close(); };
        } catch (java.io.IOException e) {
            throw new TimetableException(e.getMessage());
        }
    }
    
    /** Tisk rozvrhu 
     * @param file výstupní stream
     * @param allResources skupina zdrojů, pro které se má rozvrh vytiskoun
     * @param order pořadí
     * @param size velikost
     */
    public void print (java.io.OutputStreamWriter osw, ResourceGroup allResources, int order, int size) throws java.io.IOException {
                int nrScheduled=0;
                int writtenSlots=0;
                writeHead(osw);
                for (int i=0;i<allResources.size();i++) {
                    osw.write(subStr(allResources.get(i).shortCut,4)+"|");
                    
                    Activity[] a = new Activity[nrSlots];
                    for (int j=0;j<activities.size();j++) 
                        if (activities.get(j).isScheduled() && !activities.get(j).selectedResources.isGroup(order) && activities.get(j).selectedResources.getResource(order)==allResources.get(i)) {
                            nrScheduled++;
                            for (int k=0;k<activities.get(j).length;k++) 
                                if (a[activities.get(j).start+k]!=null) System.err.println("PRUSER");
                                else a[activities.get(j).start+k]=activities.get(j); 
                            }
                    
                    for (int j=0;j<nrSlots;j++) 
                        if (a[j]!=null) {
                            osw.write(subStr(a[j].shortCut,4)+"|");
                            writtenSlots++;
                        } else if (allResources.get(i).preference.isHARD(j))
                            osw.write("XXXX|");
                        else
                            osw.write("    |");
                    osw.write("\n");
                    for (int r=0;r<size;r++) 
                        if (r!=order) {
                            osw.write("    |");
                            for (int j=0;j<nrSlots;j++) 
                                if (a[j]!=null)
                                    osw.write(subStr(a[j].selectedResources.getResource(r).shortCut,4)+"|");
                                else
                                    osw.write("    |");
                            osw.write("\n");
                        }
                    writeLine(osw);
                        
                }
/*                for ( int i=0;i<config.pocetSlotu();i++) 
                osw.write("");
                int length=0;
                for (int i=0;i<activities.size();i++) {
                    length+=activities.get(i).length;
                    osw.write(activities.get(i).jmeno+" "+activities.get(i).length+"\n");
                }*/
    }

    /** Zápis problému(rozvrhu) do streamu
     * @param out stream
     */
   private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
       ResourceGroup allResources = new ResourceGroup();
       for (int i=0;i<resources.length;i++) 
           for (int j=0;j<resources[i].size();j++)
             allResources.add(resources[i].get(j));
       out.writeObject(config);
       allResources.write(out,activities);
           activities.write(out,allResources);
       out.writeInt(resources.length);
       for (int i=0;i<resources.length;i++) 
           resources[i].write(out,allResources);
       dependences.write(out,activities);
   }

    /** Načtení problému(rozvrhu) ze streamu
     * @param in stream
     */
   private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException, TimetableException, Exception {
       config = (Config) in.readObject();
       nrSlots=config.getInt(Config.NR_SLOTS);
       ResourceGroup allResources = ResourceGroup.read(in,config);
       activities = ActivityGroup.read(in,config,allResources);
       allResources.init(activities);
       resources = new ResourceGroup[in.readInt()];
       for (int i=0;i<resources.length;i++) 
           resources[i] = ResourceGroup.read(in,allResources);
       dependences = ActivityDependenceGroup.read(in,activities);
   }
    
   /** Uložení rozvrhovacího problému do souboru
    * @param file soubor
    */
    public void save(String file) throws Exception {
           java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(new java.io.FileOutputStream(file));
           try {
                oos.writeObject(this);
            } finally { oos.flush(); oos.close(); };
    }
    
   /** Načtení rozvrhovacího problému ze souboru
    * @param file soubor
    * @return načtená problém
    */
    public static Problem load(String file) throws Exception {
           java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new java.io.FileInputStream(file));
           Problem p=null;
           try {
                p=(Problem)ois.readObject();
            } finally { ois.close(); };
            return p;
    }
    
       
    
}
