package com.apruitt.logging;

import java.io.File;
import java.io.InputStream;
import java.io.BufferedInputStream;
import javax.sound.sampled.*;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;

/**
 * Plays a sound if a log event happens.  You should indicate the sound file via a configuration property:
 * <ul>
 * <li><code>errorFile</code> - the file to play if an <code>ERROR</code> event is received</li>
 * <li><code>warnFile</code>- the file to play if a <code>WARN</code> event is received
 * <li><code>infoFile</code>- the file to play if an <code>INFO</code> event is received
 * <li><code>debugFile</code>- the file to play if a <code>DEBUG</code> event is received
 * <li><code>defaultFile</code>- the file to play if a <code>LOG</code> event is received for which no sound has been configured
 * </ul>
 * <p>All settings are optional; if none are defined, no clip will ever play. If the clip is not available via 
 * <code>new File(filename)</code>, then the clip will be searched for via {@link ClassLoader#getResourceAsStream}. </p>
 *
 *@author andy pruitt <rapruitt@earthlink.net>
 */
public class SoundLogger extends AppenderSkeleton implements LineListener {
    private String errorFile;
    private String warnFile;
    private String infoFile;
    private String debugFile;
    private String defaultFile;

    protected void append(LoggingEvent event) {
        String id = null;
        if (Level.ERROR.equals(event.getLevel())) {
            id = errorFile;
        } else if (Level.WARN.equals(event.getLevel())) {
            id = warnFile;
        } else if (Level.INFO.equals(event.getLevel())) {
            id = infoFile;
        } else if (Level.WARN.equals(event.getLevel())) {
            id = debugFile;
        }
        if (id == null) {
            if (defaultFile == null) {
				errorHandler.error("Received event of level "+event.getLevel()
					+", but no sound clip is configured for " + event.getLevel() );
                return;
            } else {
                id = defaultFile;
            }
        }
        File file = new File(id);
        AudioInputStream audioInputStream = null;
        try {
            if (file.canRead()) {
                audioInputStream = AudioSystem.getAudioInputStream(file);
            } else {
                InputStream inst = new BufferedInputStream( getClass().getClassLoader().getResourceAsStream(id));
                audioInputStream = AudioSystem.getAudioInputStream(inst);
            }
            if (audioInputStream == null) {
                throw new Exception("Cannot get audio stream from  " + id);
            }
            play(audioInputStream);
        } catch (UnsupportedAudioFileException e) {
            errorHandler.error(file + " is an Unsupported Audio Format: " + e.getMessage());
        } catch (Exception e) {
            errorHandler.error("Error playing file " + id + ": " + e.getMessage());
        }
    }

    public void close() {          }

    public boolean requiresLayout() {
        return false;
    }

    private void play(AudioInputStream audioInputStream) {
        Clip clip = null;
        Line.Info info = new Clip.Info(Clip.class, audioInputStream.getFormat(), AudioSystem.NOT_SPECIFIED);
        try {
            clip = (Clip) AudioSystem.getLine(info);
            clip.addLineListener(this);
            clip.open(audioInputStream);
        } catch (LineUnavailableException e) {
            errorHandler.error("No sound output device is available");
            return;
        } catch (Exception e) {
            errorHandler.error("Error playing audio clip: " + e.getMessage(), e, ErrorCode.GENERIC_FAILURE);
        }
        clip.start();
        while (clip.isRunning()) {   }
        clip.stop();
        clip.close();
    }

    public void update(LineEvent event) {
        if (event.getType().equals(LineEvent.Type.STOP)) {
            Line line = event.getLine();
            line.close();
        }
    }
    /**
     * Configuration parameter.
     */
    public String getDefaultFile() {
        return defaultFile;
    }
    /**
     * Configuration parameter.
     */
    public void setDefaultFile(String defaultFile) {
        this.defaultFile = defaultFile;
    }

    /**
     * Configuration parameter.
     */
    public String getDebugFile() {
        return debugFile;
    }

    /**
     * Configuration parameter.
     */
    public void setDebugFile(String debugFile) {
        this.debugFile = debugFile;
    }

    /**
     * Configuration parameter.
     */
    public String getWarnFile() {
        return warnFile;
    }

    /**
     * Configuration parameter.
     */
    public void setWarnFile(String warnFile) {
        this.warnFile = warnFile;
    }

    /**
     * Configuration parameter.
     */
    public String getInfoFile() {
        return infoFile;
    }

    /**
     * Configuration parameter.
     */
    public void setInfoFile(String infoFile) {
        this.infoFile = infoFile;
    }

    /**
     * Configuration parameter.
     */
    public String getErrorFile() {
        return errorFile;
    }

    /**
     * Configuration parameter.
     */
    public void setErrorFile(String errorSample) {
        this.errorFile = errorSample;
    }
}
