package net.hyperic.sigar.win32;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * A simple thread that runs forever monitoring the event log.
 */
public class EventLogThread implements Runnable {

    public static final int DEFAULT_INTERVAL = 60 * 1000;

    private static Logger logger =
        Logger.getLogger(EventLogThread.class.getName());

    private Thread thread = null;
    private static HashMap logs = new HashMap();
    private boolean shouldDie = false;
    private Set notifiers     = Collections.synchronizedSet(new HashSet());

    private String logName = "Application";
    private long interval  = 10 * 1000; // Default to 10 seconds

    public static synchronized EventLogThread getInstance(String name) {
        EventLogThread instance =
            (EventLogThread)logs.get(name);

        if (instance == null) {
            instance = new EventLogThread();
            instance.setLogName(name);
            logs.put(name, instance);
        }

        return instance;
    }

    public static synchronized void closeInstances() {
        for (Iterator it = logs.values().iterator();
             it.hasNext();)
        {
            EventLogThread eventLogThread =
                (EventLogThread)it.next();
            eventLogThread.doStop();
        }
        logs.clear();
    }

    public void setInterval(long interval) {
        this.interval = interval;
    }

    public void setLogName(String logName) {
        this.logName = logName;
    }
    
    public synchronized void doStart() {
        if (this.thread != null) {
            return;
        }

        this.thread = new Thread(this, "EventLogThread");
        this.thread.setDaemon(true);
        this.thread.start();

        logger.debug(this.thread.getName() + " started");
    }

    public synchronized void doStop() {
        if (this.thread == null) {
            return;
        }
        die();
        this.thread.interrupt();
        logger.debug(this.thread.getName() + " stopped");
        this.thread = null;
    }

    public void add(EventLogNotification notifier) {
        this.notifiers.add(notifier);
    }

    public void remove(EventLogNotification notifier) {
        this.notifiers.remove(notifier);
    }

    private void handleEvents(EventLog log, int curEvent, int lastEvent)
    {
        for (int i = curEvent + 1; i <= lastEvent; i++) {

            EventLogRecord record;

            try {
                record = log.read(i);
            } catch (Win32Exception e) {
                logger.error("Unable to read event id " + i + ": " + e);
                continue;
            }

            synchronized (this.notifiers) {
                for (Iterator it = this.notifiers.iterator(); it.hasNext();)
                {
                    EventLogNotification notification =
                        (EventLogNotification)it.next();
                    if (notification.matches(record))
                        notification.handleNotification(record);
                }
            }
        }
    }

    public void run() {
        
        EventLog log = new EventLog();
        int curEvent;

        try {
            // Open the event log
            log.open(this.logName);

            curEvent = log.getNewestRecord();

            while (!shouldDie) {
                // XXX: Using the waitForChange() method would be a
                //      cleaner way to go, but we cannot interrupt
                //      a native system call.
                int lastEvent = log.getNewestRecord();
                if (lastEvent > curEvent) {
                    handleEvents(log, curEvent, lastEvent);
                }

                curEvent = lastEvent;

                try {
                    Thread.sleep(this.interval);
                } catch (InterruptedException e) {
                }
            }
            
            log.close();

        } catch (Win32Exception e) {
            logger.error("Unable to monitor event log:", e);
        } finally {
            try { log.close(); } 
            catch (Win32Exception e) {}}
    }

    public void die() {
        this.shouldDie = true;
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            args = new String[] {
                "System", "Application", "Security"
            };
        }

        EventLogNotification watcher =
            new EventLogNotification() {
                public boolean matches(EventLogRecord record) {
                    return true;
                }

                public void handleNotification(EventLogRecord record) { 
                    System.out.println(record);
                }
            };

        for (int i=0; i<args.length; i++) {
            String name = args[i];
            EventLogThread eventLogThread = 
                EventLogThread.getInstance(name);

            eventLogThread.doStart();
            eventLogThread.setInterval(1000);
            eventLogThread.add(watcher);
        }

        System.out.println("Press any key to stop");
        try {
            System.in.read();
        } catch (IOException e) { }

        EventLogThread.closeInstances();
    }
}

@@ -822,29 +822,16 @@
  static 
-synchronized 
 EventLog
@@ -895,18 +895,48 @@
 instance
- =
+;%0A%0A        synchronized (logs) %7B
 %0A       
@@ -931,32 +931,43 @@
 s) %7B%0A           
+ instance =
  (EventLogThread
@@ -976,32 +976,36 @@
 ogs.get(name);%0A%0A
+    
         if (inst
@@ -1024,32 +1024,36 @@
 ) %7B%0A            
+    
 instance = new E
@@ -1073,32 +1073,36 @@
 ();%0A            
+ 
+   
 instance.setLogN
@@ -1120,24 +1120,28 @@
             
+    
 logs.put(nam
@@ -1150,24 +1150,38 @@
  instance);%0A
+            %7D%0A
         %7D%0A%0A 
@@ -1229,29 +1229,16 @@
  static 
-synchronized 
 void clo
@@ -1249,24 +1249,58 @@
 stances() %7B%0A
+        synchronized (logs) %7B%0A    
         for 
@@ -1353,16 +1353,20 @@
         
+    
 it.hasNe
@@ -1372,34 +1372,42 @@
 ext();)%0A        
+   
+ 
 %7B%0A
+    
             Even
@@ -1442,32 +1442,36 @@
                 
+    
 (EventLogThread)
@@ -1485,32 +1485,36 @@
 ();%0A            
+ 
+   
 eventLogThread.d
@@ -1522,34 +1522,42 @@
 Stop();%0A        
-%7D%0A
+    %7D%0A    
         logs.cle
@@ -1558,24 +1558,34 @@
 gs.clear();%0A
+        %7D%0A
     %7D%0A%0A    p

synchronize on logs