/**
 * Copyright (c) 2011, Martin Foster <martin@ethereal-realms.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice, 
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright 
 *       notice, this list of conditions and the following disclaimer in the 
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Ethereal-Realms.org nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lyra.Host;

import java.lang.Exception;
import java.util.Map;

import org.lyra.Ledger;

/**
 * This class provides a direct interface to the options available for the
 * Music Player Daemon (MPD).  For the most part, this is an abstraction
 * to the <i>Terminus</i>.  
 * <p>
 * @author Martin Foster
 * @version 1.0.1, %Revision, 87%, %LastChangedDate, 28/02/11 12:52PM%
 * @see org.lyra.Host.Terminus
 */
public class Options {

	/*
	 * PACKAGE INSTANCES
	 */
	private Ledger Logger;
	private Terminus Host;
	private Map<String,String> Lang;
	
	/*
	 * CONSTANTS
	 */
	private static final String CLASS = "org.lyra.Host.Options";
	
	
	/**
	 * Primary constructor for the class.  Will establish the base environment
	 * necessary to interact with the Music Player Daemon (MPD) options.
	 * <p>
	 * @param logger Ledger instance for logging of events.
	 * @param host Host instance for interaction with the daemon.
	 * @param lang Map of language elements
	 */
	public Options(Ledger logger, Terminus host, Map<String,String> lang) {
		
		// Pass up
		this.Logger = logger;
		this.Host = host;
		this.Lang = lang;
	}
	// End of Constructor Method
	
	
	/*
	 * The following methods will accept a boolean and pass on that state to
	 * the host itself.
	 */
	
	/**
	 * Sets consume state to STATE, STATE should be true or false. When 
	 * consume is activated, each song played is removed from playlist.
	 * <p>
	 * @param state Boolean to determine state.
	 * @return Boolean on success of action.
	 */
	public boolean setConsume(boolean state) {
		
		// What shall we pass on
		String pass = (state) ? "1" : "0";
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setConsume",
				Lang.get("Options.Log.Consume")
					.replaceAll("MSTATE",pass)
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Consume", pass);
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setConsume(state);
		}
	}
	// End of setConsume Method
	

	/**
	 * Sets random state to STATE, STATE should be true or false
	 * <p>
	 * @param state Boolean to determine state.
	 * @return Boolean on success of action.
	 */
	public boolean setRandom(boolean state) {
		
		// What shall we pass on
		String pass = (state) ? "1" : "0";
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setRandom",
				Lang.get("Options.Log.Random")
					.replaceAll("MSTATE",pass)
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Random", pass);
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setRandom(state);
		}
	}
	// End of setRandom Method
	
	
	/**
	 * Sets repeat state to STATE, STATE should be true or false.
	 * <p>
	 * @param state Boolean to determine state.
	 * @return Boolean on success of action.
	 */
	public boolean setRepeat(boolean state) {
		
		// What shall we pass on
		String pass = (state) ? "1" : "0";
			
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setRepeat",
				Lang.get("Options.Log.Repeat")
					.replaceAll("MSTATE",pass)
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Repeat", pass);
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setRepeat(state);
		}
		

	}
	// End of setRepeat Method


	/**
	 * Sets single state to STATE, STATE should be true or false. When single 
	 * is activated, playback is stopped after current song, or song is 
	 * repeated if the repeat mode is enabled. 
	 * <p>
	 * @param state Boolean to determine state.
	 * @return Boolean on success of action.
	 */
	public boolean setSingle(boolean state) {
		
		// What shall we pass on
		String pass = (state) ? "1" : "0";
				
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setSingle",
				Lang.get("Options.Log.Single")
					.replaceAll("MSTATE",pass)
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Single", pass);
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setSingle(state);
		}
	}
	// End of setSingle Method
	
	
	/*
	 * These methods are mildly more complex since they require more
	 * complicated arguments to be passed.
	 */
	
	/**
	 * Sets crossfading between songs. 
	 * <p>
	 * @param crossfade Number of seconds containing crossfade value.
	 * @return Boolean on success of action.
	 */
	public boolean setCrossfade(int crossfade) {
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setCrossfade",
				Lang.get("Options.Log.Crossfade")
					.replaceAll("MDURATION", Integer.toString(crossfade))
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Crossfade", Integer.toString(crossfade));
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setCrossfade(crossfade);
		}
	}
	// End of setCrossfade Method
	
	
	/**
	 * Sets volume to VOL, the range of volume is 0-100. 
	 * <p>
	 * @param volume Range between 0-100 indicating volume.
	 * @return Boolean on success of action.
	 */
	public boolean setVolume(int volume) {
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setVolume",
				Lang.get("Options.Log.Volume")
					.replaceAll("MVOLUME", Integer.toString(volume))
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Volume", Integer.toString(volume));
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setVolume(volume);
		}
	}
	// End of setVolume Method
	
	
	/**
	 * Sets the replay gain mode. One of off, track, album.  Changing the mode 
	 * during playback may take several seconds, because the new settings does 
	 * not affect the buffered data.  
	 * <p>
	 * @param mode String value of off, track or album.
	 * @return Boolean on success of action.
	 */
	public boolean setMode(String mode) {
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"setMode",
				Lang.get("Options.Log.Mode")
					.replaceAll("MMODE", mode)
			  );
			
			// Direct pass-through
			Host.writeArgument("Mode.Mode", mode);
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return setMode(mode);
		}
	}
	// End of setMode Method
	
	
	/**
	 * Passes on the direction to the Music Player Daemon host that a database
	 * update has been requested.   This will in turn order to host to rescan
	 * for new or changed songs.
	 * <p>
	 * @return boolean indicating success of operation.
	 */
	public boolean fireUpdate() {
		
		// Attempt the connection
		try {
			
			// Log Event
			Logger.Info(
				CLASS,
				"fireUpdate",
				Lang.get("Options.Log.Update")
			  );
			
			// Direct pass-through
			Host.writeBatch("Database.Update");
			
			// Return result
			return Host.readResult();
		} 
		
		// Recover and try again
		catch (Exception e) {
			
			// Recover connect
			Host.Recover(e);
			
			// Try again
			return fireUpdate();
		}
	}
	// End of fireUpdate Method
	
}