package org.alfresco.repo.module.tool;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.schlichtherle.io.File;
import de.schlichtherle.io.FileInputStream;
import de.schlichtherle.io.FileOutputStream;

/**
 * Details of the files installed during a module installation into a WAR
 * 
 * @author Roy Wetherall
 */
public class InstalledFiles
{
    /** Modification types */
    private static final String MOD_ADD_FILE = "add";
    private static final String MOD_UPDATE_FILE = "update";
    private static final String MOD_MK_DIR = "mkdir";
    
    /** Delimieter used in the file */
    private static final String DELIMITER = "|";
    
    /** War location **/
    private String warLocation;
    
    /** Module id **/
    private String moduleId;
    
    /** Lists containing the modifications made */
    private List<String> adds = new ArrayList<String>();
    private Map<String, String> updates = new HashMap<String, String>();
    private List<String> mkdirs = new ArrayList<String>();
    
    /**
     * Constructor
     * 
     * @param warLocation   the war location
     * @param moduleId      the module id
     */
    public InstalledFiles(String warLocation, String moduleId)
    {
        this.warLocation = warLocation;
        this.moduleId = moduleId;
    }
    
    /**
     * Loads the exisiting information about the installed files from the WAR
     */
    public void load()
    {
        File file = new File(getFileLocation(), ModuleManagementTool.defaultDetector);        
        if (file.exists() == true)
        {
            try
            {
                BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
                try
                {
                    String line = reader.readLine();
                    while (line != null)
                    {
                        String[] modification = line.split("\\" + DELIMITER);
                        String mod = modification[0];
                        String location = modification[1];
                        if (mod.equals(MOD_ADD_FILE) == true)
                        {
                            this.adds.add(location);
                        }
                        else if (mod.equals(MOD_MK_DIR) == true)
                        {
                            this.mkdirs.add(location);
                        }
                        else if (mod.equals(MOD_UPDATE_FILE) == true)
                        {
                            this.updates.put(location, modification[2]);
                        }
                        line = reader.readLine();
                    }
                }
                finally
                {
                    reader.close();
                }
            }
            catch(FileNotFoundException exception)
            {
                throw new ModuleManagementToolException("The module file install file '" + getFileLocation() + "' does not exist", exception);
            }
            catch(IOException exception)
            {
                throw new ModuleManagementToolException("Error whilst reading file '" + getFileLocation(), exception);
            }
        }
    }
    
    /**
     * Saves the current modification details into the WAR
     */
    public void save()
    {
        try
        {
            File file = new File(getFileLocation(), ModuleManagementTool.defaultDetector);
            if (file.exists() == false)
            {
                file.createNewFile();               
            } 
            FileOutputStream os = new FileOutputStream(file);
            try
            {
                for (String add : this.adds)
                {
                    String output = MOD_ADD_FILE + DELIMITER + add + "\n";
                    os.write(output.getBytes());
                }
                for (Map.Entry<String, String> update : this.updates.entrySet())
                {
                    String output = MOD_UPDATE_FILE + DELIMITER + update.getKey() + DELIMITER + update.getValue() + "\n";
                    os.write(output.getBytes());
                }
                for (String mkdir : this.mkdirs)
                {
                    String output = MOD_MK_DIR + DELIMITER + mkdir + "\n";
                    os.write(output.getBytes());
                }
            }
            finally
            {
                os.close();
            }
        }
        catch(IOException exception)
        {
            throw new ModuleManagementToolException("Error whilst saving modifications file.", exception);
        }
    }
    
    /**
     * Returns the location of the modifications file based on the module id
     * 
     * @return  the file location
     */
    private String getFileLocation()
    {
        return this.warLocation + ModuleManagementTool.MODULE_DIR + "/" + this.moduleId + "/modifications.install";
    }
    
    /**
     * Get all the added files
     * 
     * @return list of files added to war
     */
    public List<String> getAdds()
    {
        return adds;
    }
    
    /** 
     * Get all the updated files, key is the file that has been updated and the value is the 
     * location of the backup made before modification took place.
     * 
     * @return  map of file locaiton and backup
     */
    public Map<String, String> getUpdates()
    {
        return updates;
    }
    
    /**
     * Gets a list of the dirs added during install
     * 
     * @return  list of directories added
     */
    public List<String> getMkdirs()
    {
        return mkdirs;
    }
    
    /**
     * Add a file addition
     * 
     * @param location  the file added
     */
    public void addAdd(String location)
    {
        this.adds.add(location);
    }
    
    /**
     * Add a file update
     * 
     * @param location  the file updated
     * @param backup    the backup location
     */
    public void addUpdate(String location, String backup)
    {
        this.updates.put(location, backup);
    }
    
    /**
     * Add a directory 
     * 
     * @param location  the directory location
     */
    public void addMkdir(String location)
    {
        this.mkdirs.add(location);
    }
}