/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * END COPYRIGHT BLOCK **/
package com.netscape.management.admserv.logging;

import java.io.*;
import java.util.*;
import java.net.*;
import javax.swing.table.*;
import com.netscape.management.client.comm.*;
import com.netscape.management.client.console.*;
import com.netscape.management.client.logging.*;
import com.netscape.management.client.util.*;
import com.netscape.management.admserv.*;

public class AccessLogViewerModel extends LogViewerModel {
    ConsoleInfo _consoleInfo;
    String _url;
    String _username;
    String _password;
    int _logLength = 0;
    private boolean _isInitialized = false;
    public boolean _logLengthAvailable = false;
    public boolean _logLengthCalculating = false;
    public StringBuffer _logLengthStringBuffer = null;
    public static ResourceSet _resource = AdminServer._resource;

    public AccessLogViewerModel(ConsoleInfo consoleInfo, String url) {
        super();
        _consoleInfo = consoleInfo;
        //_url = "http://gumby.mcom.com:666/admin-serv/tasks/Configuration/ReadLog"; //_consoleInfo.getAdminURL() + url;
        //_username = "uid=admin, ou=People, o=airius.com"; //_consoleInfo.getAuthenticationDN();
        //_password = "admin"; //_consoleInfo.getAuthenticationPassword();
        _url = _consoleInfo.getAdminURL() + url;
        _username = _consoleInfo.getAuthenticationDN();
        _password = _consoleInfo.getAuthenticationPassword();

        addColumn(_resource.getString("log", "lineno"));
        addColumn(_resource.getString("log", "host"));
        addColumn(_resource.getString("log", "username"));
        addColumn(_resource.getString("log", "date"));
        addColumn(_resource.getString("log", "time"));
        addColumn(_resource.getString("log", "header"));
        addColumn(_resource.getString("log", "errorcode"));
        addColumn(_resource.getString("log", "length"));
        _isInitialized = true;
    }

    public void populateRows(int rowStartIndex, int numRows) {
        Debug.println(6,
                "AccessLogViewer: populateRows " + rowStartIndex +
                " to " + (rowStartIndex + numRows));
        try {
            URL url = new URL(_url + "?op=read" + "&start=" +
                    rowStartIndex + "&stop=" +
                    (rowStartIndex + numRows) + "&name=access");
            HttpManager _httpManager = new HttpManager();
            AccessDataCommClient commClient =
                    new AccessDataCommClient(this, _username, _password);
            _httpManager.get(url, commClient, (ILogViewerModel) this,
                    CommManager.ASYNC_RESPONSE |
                    CommManager.FORCE_BASIC_AUTH);
        } catch (Exception e) {
            Debug.println(4, "AccessLogViewer: populateRows() " + e);
        }
    }



	/**
      * Returns the number of rows in log file
      */
    public int getLogLength() {
        if (!_isInitialized)
            return -1;

        long _currentTimeMillis = System.currentTimeMillis();
        if (((_currentTimeMillis - _lastLogLengthCheckTime) >
                UPDATE_INTERVAL) && !_logLengthCalculating) {
            _lastLogLengthCheckTime = _currentTimeMillis;
            try // to update _logLength
            {
                _logLengthCalculating = true;
                _logLengthAvailable = false;
                _logLengthStringBuffer = new StringBuffer();
                URL url = new URL(_url + "?op=count" + "&name=access");
                HttpManager _httpManager = new HttpManager();
                LogLengthCommClient commClient =
                        new AccessLogLengthCommClient(this, _username, _password);
                _httpManager.get(url, commClient, _logLengthStringBuffer,
                        CommManager.ASYNC_RESPONSE |
                        CommManager.FORCE_BASIC_AUTH);
            } catch (Exception e) {
                _logLengthCalculating = false;
                Debug.println(4, "AccessLogViewer: getLogLength(): " + e);
            }
        }

        if (_logLengthAvailable) {
            try {
                _logLength = Integer.parseInt(
                        _logLengthStringBuffer.toString());
                Debug.println(6,
                        "AccessLogViewer: _logLength=" + _logLength);
            } catch (NumberFormatException e) {
                Debug.println(4,
                        "AccessLogViewer: Cannot convert log length: " +
                        _logLengthStringBuffer.toString());
            }
        }

        return _logLength;
    }

}

// Having AccessLogData as an inner-class of AccessLogViewerModel causes
// compilation errors on NT when the build tree is sufficiently deep.
// for example: D:\admin\admin50\builds\20000326.1\ztNT4.0\
class AccessLogData {
    StringBuffer host = new StringBuffer();
    StringBuffer username = new StringBuffer();
    StringBuffer date = new StringBuffer();
    StringBuffer time = new StringBuffer();
    StringBuffer header = new StringBuffer();
    StringBuffer httpcode = new StringBuffer();
    StringBuffer datalength = new StringBuffer();
}

// Having AccessDataCommClient as an inner-class of AccessLogViewerModel causes
// compilation errors on NT when the build tree is sufficiently deep.
// for example: D:\admin\admin50\builds\20000326.1\ztNT4.0\
class AccessDataCommClient extends AbstractCommClient {
    final int STATE_HOST = 0;
    final int STATE_USERNAME = 1;
    final int STATE_DATE = 2;
    final int STATE_TIME = 3;
    final int STATE_PRE_HEADER = 4;
    final int STATE_HEADER = 5;
    final int STATE_PRE_HTTPCODE = 6;
    final int STATE_HTTPCODE = 7;
    final int STATE_DATALENGTH = 8;
    final int STATE_EXTRA = 9;
	AccessLogViewerModel model;

    public AccessDataCommClient(AccessLogViewerModel model, String username, String password) {
        super(username, password);
		this.model = model;
        Debug.println(6, "AccessLogViewer: AccessDataCommClient.<init>");
    }

    protected AccessLogData parse(String logEntry) {
        StringBuffer buffer = new StringBuffer(logEntry);
        AccessLogData data = new AccessLogData();
        int bufferLength = buffer.length();
        int state = STATE_HOST;
        char character;
        char charNMinus1 = 0;
        char charNMinus2 = 0;

        for (int index = 0; index < bufferLength; index++) {
            character = buffer.charAt(index);
            if (index > 0) {
                charNMinus1 = buffer.charAt(index-1);
            }
            if (index > 1) {
                charNMinus2 = buffer.charAt(index-2);
            }
            switch (state) {
            case STATE_HOST:
                if ((character == ' ') && (charNMinus1 == '-') &&
                    (charNMinus2 == ' ')) {
                    state = STATE_USERNAME;
                }
                else if ((character != ' ') && (charNMinus1 != ' '))
                    data.host.append(character);
                break;

            case STATE_USERNAME:
                if (character == '[')
                    state = STATE_DATE;
                else if (character != ' ')
                    data.username.append(character);
                break;

            case STATE_DATE:
                if (character == ':')
                    state = STATE_TIME;
                else
                    data.date.append(character);
                break;

            case STATE_TIME:
                if (character == ']')
                    state = STATE_PRE_HEADER;
                else
                    data.time.append(character);
                break;

            case STATE_PRE_HEADER:
                if (character == '"')
                    state = STATE_HEADER;
                break;

            case STATE_HEADER:
                if (character == '"')
                    state = STATE_PRE_HTTPCODE;
                else
                    data.header.append(character);
                break;

            case STATE_PRE_HTTPCODE:
                if (character == ' ')
                    state = STATE_HTTPCODE;
                break;

            case STATE_HTTPCODE:
                if (character == ' ')
                    state = STATE_DATALENGTH;
                else
                    data.httpcode.append(character);
                break;

            case STATE_DATALENGTH:
                if (character == ' ')
                    state = STATE_EXTRA;
                else
                    data.datalength.append(character);
                break;


            case STATE_EXTRA:
                // TODO
                break;

            }
        }
        return data;
    }

    public void replyHandler(InputStream replyStream, CommRecord cr) {
        BufferedReader replyBuffer = new BufferedReader(
                new InputStreamReader(replyStream));
        ILogViewerModel model = (ILogViewerModel) cr.getArg();
        int rowOffset = model.getRowOffset();
        int bufferLength = model.getBufferLength();
        String logEntry = null;
        for (int rowIndex = 0; rowIndex < bufferLength; rowIndex++) {
            try {
                logEntry = replyBuffer.readLine();
            } catch (Exception e) {
                Debug.println(4,
                        "AccessLogViewer: replyHandler() " + e);
            }
            if (logEntry != null) {
                AccessLogData d = parse(logEntry);
                model.setValueAt(
                        Integer.toString(rowOffset + rowIndex),
                        rowIndex, 0);
                model.setValueAt(d.host, rowIndex, 1);
                model.setValueAt(d.username, rowIndex, 2);
                model.setValueAt(d.date, rowIndex, 3);
                model.setValueAt(d.time, rowIndex, 4);
                model.setValueAt(d.header, rowIndex, 5);
                model.setValueAt(d.httpcode, rowIndex, 6);
                model.setValueAt(d.datalength, rowIndex, 7);
            } else {
                break;
            }
        }
        finish();
    }

    public synchronized void finish() {
        super.finish();
        model.tableDataChanged();
        Debug.println(9, "AccessLogViewer: AccessDataCommClient.finish()");
    }
}

// Having AccessDataCommClient as an inner-class of AccessLogViewerModel causes
// compilation errors on NT when the build tree is sufficiently deep.
// for example: D:\admin\admin50\builds\20000326.1\ztNT4.0\
class AccessLogLengthCommClient extends LogLengthCommClient 
{
	AccessLogViewerModel model;
	
    public AccessLogLengthCommClient(AccessLogViewerModel model, String username, String password) {
        super(username, password);
		this.model = model;
        Debug.println(9, "AccessLogViewer: AccessLogLengthCommClient.<init>");
    }

    public synchronized void finish() {
        super.finish();
        model._logLengthAvailable = true;
        model._logLengthCalculating = false;
        model.tableDataChanged();
        Debug.println(9, "AccessLogViewer: AccessLogLengthCommClient.finish()");
    }
}
