/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hudson.plugins.disk_usage;

import com.google.common.collect.Maps;
import hudson.BulkChange;
import hudson.XmlFile;
import hudson.model.AbstractBuild;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.Saveable;
import hudson.model.listeners.SaveableListener;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;

/**
 *
 * @author Lucie Votypkova
 */
public class ProjectDiskUsage implements Saveable{
    
    protected transient Job job;
    protected Long diskUsageWithoutBuilds = 0l;
    protected Map<String,Map<String,Long>> slaveWorkspacesUsage = new ConcurrentHashMap<String,Map<String,Long>>();
    private Set<DiskUsageBuildInformation> buildDiskUsage;
   
    
    public Map<String,Map<String,Long>> getSlaveWorkspacesUsage(){
        return Maps.newHashMap(slaveWorkspacesUsage);
    }
            
     public XmlFile getConfigFile(){
        return new XmlFile(new File(job.getRootDir(), "disk-usage.xml"));
    }
     
     public void setProject(Job job){
         this.job = job;
     }
     
     public boolean isBuildsLoaded(){
         return buildDiskUsage!=null;
     }
     
     public Set<DiskUsageBuildInformation> getBuildDiskUsage(){
         if(buildDiskUsage==null){
             try{
                loadFirstTime();
             }
             catch(Exception e){
                 return new HashSet<DiskUsageBuildInformation>();
             }
         }
         return buildDiskUsage;
     }
    
    public synchronized void save() {
        if(BulkChange.contains(this))   return;
        try {
            getConfigFile().write(this);
            SaveableListener.fireOnChange(this, getConfigFile());
        } catch (IOException e) {
            Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to save "+getConfigFile(),e);
        }
    }
    
    public synchronized void loadFirstTime(){
        load();
        buildDiskUsage = new HashSet<DiskUsageBuildInformation>();
        List<Run> list = job.getBuilds();
        for(Run run : list){
            if(run instanceof AbstractBuild){
                AbstractBuild build = (AbstractBuild) run;
                BuildDiskUsageAction usage = run.getAction(BuildDiskUsageAction.class);
                DiskUsageBuildInformation information = new DiskUsageBuildInformation(build.getId(), build.getTimeInMillis(), build.number, 0l);
                buildDiskUsage.add(information);
                if(usage==null){
                    run.getActions().add(new BuildDiskUsageAction(build));
                    try {
                        build.save();
                    } catch (IOException ex) {
                        Logger.getLogger(ProjectDiskUsage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                else{
                    information.setSize(usage.buildDiskUsage);
                }
                DiskUsageUtil.addBuildDiskUsageAction(build);
            }
        }
        DiskUsageProperty property = (DiskUsageProperty) job.getProperty(DiskUsageProperty.class);
        property.checkWorkspaces(true);
        save();
    }
    
    public synchronized void load(){
            XmlFile file = getConfigFile();
            if(!file.getFile().exists()){
                return;
            }
            try {
                file.unmarshal(this);
            } catch (IOException e) {
                Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to load "+file, e);
            }
//        if(buildDiskUsage==null){
//             //seems like it needs load old data
//             loadOldData();
//        }
//        removeDeletedBuilds();
    }
    
    /**
     * IT is only for backward compatibility to load old data. It breaks lazy loading. 
     * Should be used only one times - updating of plugin
     * 
     * @deprecated
     * 
     */
    public void loadOldData(){
        buildDiskUsage = new HashSet<DiskUsageBuildInformation>();
        List<Run> list = job.getBuilds();
        for(Run run : list){
            if(run instanceof AbstractBuild){
                AbstractBuild build = (AbstractBuild) run;
                BuildDiskUsageAction usage = run.getAction(BuildDiskUsageAction.class);
                DiskUsageBuildInformation information = new DiskUsageBuildInformation(build.getId(), build.getTimeInMillis(), build.number, 0l);
                buildDiskUsage.add(information);
                if(usage!=null){
                    information.setSize(usage.buildDiskUsage);
                    run.getActions().remove(usage);
                }
            }
        }
        save();
    }   
    
    private void removeDeletedBuilds(){
        Iterator<DiskUsageBuildInformation> iterator= buildDiskUsage.iterator();
        while(iterator.hasNext()){
            DiskUsageBuildInformation information = iterator.next();
            File buildDir = new File(Jenkins.getInstance().getBuildDirFor(job), information.getId());
            if(!buildDir.exists())
                buildDiskUsage.remove(information);
        }
    }
}
