package com.jscape.inet.ftp;

import java.io.BufferedReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * Parser responsible for handling FTP directory listing returned in AS400 format.
 * The following is a partial example listing of the format expected.
 * <p/>
 * <pre>
 *   agonzalez   1148    07/12/06  17:15:29   *DIR    Archive/
 *   agonzalez   1288    08/07/06  14:19:08   *STMF   eye.asc
 *   agonzalez   4096    01/01/70  02:00:00   *STMF   jaase
 * </pre>
 * <p/>
 * It ignores the i5 object groups entries and only parses the *DIR and *STMF entries
 */
@SuppressWarnings("unchecked")
public class AS400Parser
        implements FtpFileParser {
    /**
     * It is the token to file entry
     */
    private static final String FILE_TOKEN = "*STMF";

    /**
     * It is the the toke to directory entry
     */
    private static final String DIRECTORY_TOKEN = "*DIR";

    /**
     * Parses directory listing.
     * Reads directory listing from FTP server and parses contents into an Enumeration of FtpFile.
     *
     * @param in the BufferedReader passed in by Ftp class when invoking Ftp#getDirListing method.
     *
     * @return an Enumeration of FtpFile
     *
     * @see FtpFile
     * @see Ftp#getDirListing
     */
    public Enumeration parse(BufferedReader in)
            throws IOException {
        Vector files = new Vector();
        String line;
        while ((line = in.readLine()) != null) {
            FtpFile file = parseLine(line);
            if (file != null) {
                files.add(file);
            }
        }
        return files.elements();
    }

    /**
     * Parses a line from a directory listing input which contains AS400 fortma
     *
     * @param line The line will be parsed
     *
     * @return Returns a FtpFile which contains information about the file parsed
     */
    private FtpFile parseLine(String line) {
        // owner size date time *DIR   name
        // owner size date time *STMF  name

        StringTokenizer tokenizer = new StringTokenizer(line);
        int tokenCount = tokenizer.countTokens();

        if (tokenCount < 6) {
            return (FtpFile) null;
        }

        FtpFile file = new FtpFile("", line, this);

        file.setGroup(tokenizer.nextToken());

        file.setFilesize(tokenizer.nextToken());

        file.setDate(tokenizer.nextToken());

        file.setTime(tokenizer.nextToken());

        String typeElement = tokenizer.nextToken();
        if (isI5ObjectGroup(typeElement)) {
            return (FtpFile) null;
        }

        file.setDirectory(isDirectory(typeElement));

        String fileName = parseFileName(tokenizer);
        file.setFilename(fileName);
        return file;
    }

    /**
     * The rest of the line is included as file name part
     *
     * @param tokenizer The tokenize to parse the dir listing line
     *
     * @return The file name
     */
    private String parseFileName(StringTokenizer tokenizer) {
        String fileName = "";
        while (tokenizer.hasMoreElements()) {
            fileName = fileName + " " + tokenizer.nextToken();
        }
        return fileName.trim();
    }

    /**
     * Returns <code>true</code> if the line entry is a directory
     *
     * @param directoryData The token data which indicates if the entry  is one file or directory
     *
     * @return <code>true</code> is the entry is a directory
     */
    private boolean isDirectory(String directoryData) {
        boolean isDirectory = false;

        if (!directoryData.startsWith(FILE_TOKEN)) {
            isDirectory = true;
        }
        return isDirectory;
    }

    /**
     * Gets if the token is i5 object groups which should be ignored by the parser
     *
     * @param typeElement The current token that is being parsed
     *
     * @return <code>true</code> if the token is a i5 object groups otherwise it returns
     *         <code>false</code> when the token is *DIR or *STMF
     */
    private boolean isI5ObjectGroup(String typeElement) {
        boolean i5ObjectGroup = true;
        if (typeElement.equals(DIRECTORY_TOKEN)
                || typeElement.equals(FILE_TOKEN)) {
            i5ObjectGroup = false;
        }
        return i5ObjectGroup;
    }

    /**
     * Parses a string which contains a date and returns a date object
     *
     * @param date The string will be parsed
     *
     * @return A object date which corresponds with the input string argument
     *
     * @throws ParseException if an error ocurrs
     */
    public Date getFileDate(String date)
            throws ParseException {
        // Entry spected
        // 07/12/06  17:15:29
        Date fileDate = null;
        try {
            SimpleDateFormat format = new SimpleDateFormat("dd/MM/yy");
            fileDate = format.parse(date);
        } catch (ParseException e) {
            SimpleDateFormat format = new SimpleDateFormat("MM/dd/yy");
            fileDate = format.parse(date);
        }
        return fileDate;
    }

    /**
     * Gets the date and time for an FtpFile entry sourced from an AS400 directory listing.
     *
     * @param file the FtpFile
     *
     * @throws ParseException if an error occurs while parsing entry
     * @see FtpFile
     */
    public Date getDateTime(FtpFile file)
            throws ParseException {
        Date fileDate = null;
        try {
            SimpleDateFormat format = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
            fileDate = format.parse(file.getDate() + " " + file.getTime());
        } catch (ParseException e) {
            SimpleDateFormat format = new SimpleDateFormat("MM/dd/yy HH:mm:ss");
            fileDate = format.parse(file.getDate() + " " + file.getTime());
        }
        return fileDate;
    }


}
