/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2011 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.addon.deploy.impl.util;

import org.ow2.jonas.addon.deploy.api.deployer.IAddonDeployerLog;
import org.ow2.jonas.addon.deploy.api.util.IAddonLogEntry;
import org.ow2.jonas.lib.work.AbsDeployerLog;
import org.ow2.jonas.workcleaner.DeployerLogException;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * Class which permits to store or load the association between the name of an Addon, the name of a package and the
 * timestamped work copy associated.
 * @author Jeremy Cazaux
 */
public class AddonDeployerLog extends AbsDeployerLog<IAddonLogEntry> implements IAddonDeployerLog<IAddonLogEntry> {

    /**
     * Constructor for the deployerLog.
     * @param logFile the file which is used for read/write entries
     * @throws DeployerLogException if the loadentries failed.
     */
    public AddonDeployerLog(final File logFile) throws DeployerLogException {
        super(logFile);
    }

    /**
     * load the entries of the log file.
     * @throws DeployerLogException if the load failed.
     */
    @Override
    protected synchronized void loadEntries() throws DeployerLogException {

        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(logFile));
        } catch (FileNotFoundException e) {
            throw new DeployerLogException("Can not read the " + logFile + " file");
        }
        String line = null;

        String field = null;
        String name = null;
        int state;
        boolean isInstalledFromACommand;
        File originalField = null;
        File copyField = null;
        StringTokenizer st = null;

        try {
            //Read the text file
            while ((line = br.readLine()) != null) {

                //parse the String
                st = new StringTokenizer(line, SEPARATOR_ENTRY);
                name = st.nextToken();
                if (name == null) {
                    throw new DeployerLogException("Inconsistent line in the file " + logFile);
                }

                field = st.nextToken();
                if (field == null) {
                    throw new DeployerLogException("Inconsistent line in the file " + logFile);
                }
                try {
                    state = Integer.parseInt(field);
                } catch (NumberFormatException e) {
                    throw new DeployerLogException("Inconsistent line in file " + logFile +
                            ". The state could not be parsed as an int value.");
                }

                field = st.nextToken();
                if (field == null) {
                    throw new DeployerLogException("Inconsistent line in the file " + logFile);
                }
                try {
                    isInstalledFromACommand = Boolean.parseBoolean(field);
                } catch (NumberFormatException e) {
                    throw new DeployerLogException("Inconsistent line in file " + logFile);
                }


                field = st.nextToken();
                if (field == null) {
                    throw new DeployerLogException("Inconsistent line in the file " + logFile);
                }

                originalField = new File(field);

                field = st.nextToken();
                if (field == null) {
                    throw new DeployerLogException("Inconsistent line in the file " + logFile);
                }

                copyField = new File(field);

                logger.debug("Entry[originalField=" + originalField + ",copyField=" + copyField + "]");

                IAddonLogEntry addonLogEntry = new AddonLogEntry(name, state, isInstalledFromACommand, originalField, copyField);
                this.logEntries.add(addonLogEntry);

            }
            // Close the input stream
            br.close();
        } catch (IOException ioe) {
            throw new DeployerLogException("Error while reading the log file " + logFile + " :" + ioe.getMessage());
        }
    }

    /**
     * Dump(save) the entries to the log file.
     * @throws DeployerLogException if the save failed.
     */
    @Override
    protected synchronized void saveEntries() throws DeployerLogException {

        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new BufferedWriter(new FileWriter(logFile)));
        } catch (IOException e) {
            throw new DeployerLogException("Problem while trying to get an output stream for the " + logFile + " file");
        }

        IAddonLogEntry logEntry = null;
        String name = null;
        String original = null;
        String copy = null;
        String line = null;
        int state;
        boolean isInstalledFromACommand = false;
        for (Enumeration<IAddonLogEntry> e = logEntries.elements(); e.hasMoreElements();) {
            logEntry = e.nextElement();

            //get the infos
            try {
                name = logEntry.getName();
                state = logEntry.getState();
                isInstalledFromACommand = logEntry.isInstalledFromACommand();
                original = logEntry.getOriginal().getCanonicalPath();
                copy = logEntry.getCopy().getCanonicalPath();
            } catch (IOException ioe) {
                throw new DeployerLogException("Problem while trying to get files names ");
            }

            //create the line
            line = name + SEPARATOR_ENTRY + String.valueOf(state) + SEPARATOR_ENTRY + String.valueOf(isInstalledFromACommand)
                    + SEPARATOR_ENTRY + original + SEPARATOR_ENTRY + copy;

            //dump the line
            pw.println(line);
        }
        // Close the stream
        pw.close();
    }

    /**
     * Add the entry and return the new entries.
     * @param logEntry the entry to add
     * @throws DeployerLogException if the add can't be done
     */
    @Override
    public Vector<IAddonLogEntry> addEntry(final IAddonLogEntry logEntry) throws DeployerLogException {
        if (logEntries == null) {
            throw new DeployerLogException("Can not add an entry, the vector is null");
        }

        //add only if it's not already present
        String name = null;
        File originalEntry = null;
        File copyEntry = null;

        boolean found = false;
        Enumeration<IAddonLogEntry> e = logEntries.elements();

        //add only if the entry is not found
        while (e.hasMoreElements() && !found) {
            IAddonLogEntry entry = e.nextElement();
            name = entry.getName();
            originalEntry = entry.getOriginal();
            copyEntry = entry.getCopy();

            if (originalEntry.getPath().equals(logEntry.getOriginal().getPath())
                    && copyEntry.getPath().equals(logEntry.getCopy().getPath())
                    && name.equals(logEntry.getName())) {
                found = true;
            }
        }
        if (found) {
            return logEntries;
        }

        //add can be done
        logEntries.add(logEntry);

        //write to the file.
        saveEntries();

        //return the new vector
        return logEntries;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized Vector<IAddonLogEntry> addEntry(final String name,
                                                        final int state,
                                                        final boolean isInstalledFromACommand,
                                                        final File original,
                                                        final File copy)
            throws DeployerLogException {

        if (logEntries == null) {
            throw new DeployerLogException("Can not add an entry, the vector is null");
        }

        //add only if it's not already present
        return addEntry(new AddonLogEntry(name, state, isInstalledFromACommand, original, copy));
    }

    /**
     * @param original Original File
     * @return the entry which match with the orginal file
     */
    @Override
    public IAddonLogEntry getEntry(final File original) {

        for (IAddonLogEntry logEntry: this.logEntries) {

            if (logEntry.getOriginal().equals(original)) {
                return logEntry;
            }
        }

        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void updateEntry(final String name, final int state) throws DeployerLogException {
       IAddonLogEntry logEntry = getEntry(name);
       if (logEntry == null) {
           throw new DeployerLogException("Could not update the entry " + name);
       }
       logEntry.updateState(state);
       saveEntries();
    }

    /**
     * @param name the name of an addon
     * @return the correct entry
     */
    private IAddonLogEntry getEntry(final String name) {
        for (IAddonLogEntry logEntry: this.logEntries) {
            if (logEntry.getName().equals(name)) {
                return logEntry;
            }
        }
        return null;
    }
}
