/*
 * @(#)VMSParser.java
 *
 * Copyright (c) 2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */
package com.jscape.inet.ftp;

import java.io.BufferedReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Parser responsible for handling FTP directory listing returned in VMS format.
 * The following is a partial example listing of the format expected.
 * <p/>
 * <pre>
 * ANONYMOUS:[000000]
 *
 * .MESSAGE;4               1  1-MAR-1997 09:49 [1,1] (RWED,RWED,RE,RE)
 * 000ARCHIVES.TXT;3        2  1-MAR-1997 09:57 [1,1] (RWED,RWED,RE,RE)
 * CGI.DIR;1                1 19-NOV-2001 21:10 [1,1] (RWE,RWE,RE,RE)
 * CONTRIB.DIR;1            1 19-NOV-2001 21:10 [1,1] (RWE,RWE,RE,RE)
 * </pre>
 */
@SuppressWarnings("unchecked")
public class VMSParser
        implements FtpFileParser {

    private static final int BLOCK_SIZE = 512;

    /**
     * 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) {
            if (!line.equals("")
                    && !line.startsWith("Directory")
                    && !line.startsWith("Total")
                    && !line.endsWith("]")) {

                FtpFile file = null;

                // check if entry spans multiple lines
                if (line.endsWith(")") || line.endsWith(">")) {
                    file = parseLine(line);
                } else {
                    StringBuffer buffer = new StringBuffer(line);
                    // read and append until line ends with )
                    String tmp = null;
                    while ((tmp = in.readLine()) != null) {
                        buffer.append(tmp);
                        if (tmp.endsWith(")")) {
                            break;
                        }
                    }
                    file = parseLine(buffer.toString());
                }


                if (!(file == null)) {
                    files.addElement(file);
                }
            }
        }

        return files.elements();
    }

    /**
     * Gets the date and time for an FtpFile entry sourced from an VMS directory listing.
     *
     * @param file   the FtpFile
     * @param zone   the TimeZone to use when calculating time. Your default timezone is used if not provided.
     * @param locale the Locale to use when parsing date. Locale.US is used if not provided.
     *
     * @return a Date, or null if FtpFile#getDate or FtpFile#getTime values are null
     *
     * @throws ParseException if an error occurs while parsing entry
     */
    public Date getDateTime(FtpFile file, TimeZone zone, Locale locale)
            throws ParseException {
        SimpleDateFormat df = null;
        Date d = null;
        String format = "d-MMM-yyyy HH:mm";
        String date = file.getDate();
        String time = file.getTime();

        if (date == null || time == null) {
            return d;
        }

        if (locale != null) {
            df = new SimpleDateFormat(format, locale);
        } else {
            df = new SimpleDateFormat(format, Locale.US);
        }

        if (zone != null) {
            df.setTimeZone(zone);
        }

        d = df.parse(file.getDate() + " " + file.getTime());
        return d;
    }

    /**
     * Gets the date and time for an FtpFile entry sourced from an NT directory listing.
     *
     * @param file the FtpFile
     *
     * @throws ParseException if an error occurs while parsing entry
     * @see FtpFile
     */
    public Date getDateTime(FtpFile file)
            throws ParseException {
        return getDateTime(file, null, null);
    }

    private FtpFile parseLine(String line) {
        FtpFile file = null;
        String name = null;
        String tmp = null;
        boolean directory = false;
        String version = "";
        String size = "";
        String date = "";
        String time = "";
        String group = "";
        String owner = "";
        String permission = "";
        int currentIndex = 0;

        int nameIndex = line.indexOf(";");
        if (nameIndex == -1) {
            return null;
        } else {
            // get file name and check if directory
            try {
                name = line.substring(0, nameIndex);
                if (name.trim().endsWith(".DIR")) {
                    directory = true;
                    name = name.substring(0, name.indexOf(".DIR"));
                }
                file = new FtpFile(name, line, this);
                file.setDirectory(directory);
            } catch (Exception e) {
                // todo: ignore?
            }

            // get version number
            try {
                tmp = line.substring(nameIndex + 1);
                for (int i = 0;
                     i < tmp.length();
                     ++i) {
                    char c = tmp.charAt(i);
                    if (Character.isDigit(c)) {
                        version += c;
                    } else {
                        currentIndex = i + 1;
                        break;
                    }
                }

                // set current index
                if (currentIndex == 0) {
                    currentIndex = tmp.length() - 1;
                }
            } catch (Exception e) {
                // todo: ignore?
            } finally {
                // reset tmp variable and currentIndex
                if (tmp != null) {
                    tmp = tmp.substring(currentIndex);
                }

                currentIndex = 0;
            }


            // parse filesize
            boolean readSize = false;
            boolean readBlocksUsed = false;
            for (int i = 0;
                 i < tmp.length();
                 ++i) {
                char c = tmp.charAt(i);

                // reading size until whitespace encountered
                if (!readBlocksUsed) {
                    if (c == '/') {
                        readBlocksUsed = true;
                    } else if (
                            Character.isWhitespace(c) && size.length() > 0) {
                        currentIndex = i;
                        break;
                    } else if (Character.isWhitespace(c)) {
                        continue;
                    } else if (Character.isDigit(c)) {
                        size += c;
                    }
                } else {
                    if (Character.isWhitespace(c)) {
                        currentIndex = i;
                        break;
                    }
                }
            }

            // set filesize
            if (file != null) {
                try {
                    long actualSize = BLOCK_SIZE * Integer.parseInt(size);
                    file.setFilesize(Long.toString(actualSize));
                } catch (Exception e) {
                    return file;
                }
            }

            // reset tmp variable and currentIndex
            tmp = tmp.substring(currentIndex);
            currentIndex = 0;

            // parse date
            for (int i = 0;
                 i < tmp.length();
                 ++i) {
                char c = tmp.charAt(i);
                if (Character.isWhitespace(c)) {
                    if (date.length() > 0) {
                        currentIndex = i;
                        break;
                    } else {
                        continue;
                    }
                } else {
                    date += c;
                }
            }

            // set date
            file.setDate(date);

            // reset tmp variable and currentIndex
            tmp = tmp.substring(currentIndex);
            currentIndex = 0;

            // parse time
            try {
                for (int i = 0;
                     i < tmp.length();
                     ++i) {
                    char c = tmp.charAt(i);
                    if (Character.isWhitespace(c)) {
                        if (time.length() > 0) {
                            currentIndex = i;
                            break;
                        } else {
                            continue;
                        }
                    } else {
                        time += c;
                    }
                }

                // set time
                file.setTime(time);
            } catch (Exception e) {
                // todo: ignore?
            } finally {
                // reset tmp variable and currentIndex
                tmp = tmp.substring(currentIndex);
                currentIndex = 0;
            }


            // parse group and owner
            try {
                String groupOwner = "";
                for (int i = 0;
                     i < tmp.length();
                     ++i) {
                    char c = tmp.charAt(i);
                    if (Character.isWhitespace(c)) {
                        if (groupOwner.length() > 0) {
                            currentIndex = i;
                            break;
                        } else {
                            continue;
                        }
                    } else {
                        groupOwner += c;
                    }
                }

                // trim off left and right brackets
                groupOwner = groupOwner.substring(1, groupOwner.length() - 1);
                group = groupOwner.substring(0, groupOwner.indexOf(","));
                owner = groupOwner.substring(groupOwner.indexOf(",") + 1);

                // set group and owner
                file.setGroup(group);
                file.setOwner(owner);
            } catch (Exception e) {
                // todo: ignore?
            } finally {
                // reset tmp variable and currentIndex
                tmp = tmp.substring(currentIndex);
                currentIndex = 0;
            }

            // parse permissions
            try {
                for (int i = 0;
                     i < tmp.length();
                     ++i) {
                    char c = tmp.charAt(i);
                    if (Character.isWhitespace(c)) {
                        if (permission.length() > 0) {
                            currentIndex = i;
                            break;
                        } else {
                            continue;
                        }
                    } else {
                        permission += c;
                    }
                }

                // set permissions
                file.setPermission(permission);

            } catch (Exception e) {
                // todo: ignore?
            } finally {
                // reset tmp variable and currentIndex
                tmp = tmp.substring(currentIndex);
                currentIndex = 0;
            }
            return file;
        }
    }

    /**
     * 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 {
        // Format spected 1-MAR-1997 09:49
        Date fileDate = null;
        try {
            SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy", Locale.US);
            fileDate = format.parse(date);
        } catch (ParseException e) {
            SimpleDateFormat format = new SimpleDateFormat("MM-dd-yyyy", Locale.US);
            fileDate = format.parse(date);
        }
        return fileDate;
    }
}
