/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.unix.strftime;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

public final class Strftime {

	static enum S1 { INIT, FLAG }

	private Map<Integer, StrftimeAction> actions;
	private int fchr;

	/**
	 * 
	 * @param fchr
	 * @param actions
	 */
	public Strftime(int fchr, Map<Integer, StrftimeAction> actions) {
		this.fchr = fchr;
		this.actions = new HashMap<Integer, StrftimeAction>(actions);
	}

	//
	Strftime(int fchr, Map<Integer, StrftimeAction> actions, int d) {
		this.fchr = fchr;
		this.actions = actions;
	}

	/**
	 * 
	 * @return
	 */
	public static Strftime getInstance() {
		Map<Integer, StrftimeAction> a;

		a = new HashMap<Integer, StrftimeAction>();
		a.put((int)'a', StrftimeSimpleDateAction.ACTION_a);
		a.put((int)'A', StrftimeSimpleDateAction.ACTION_A);
		a.put((int)'b', StrftimeSimpleDateAction.ACTION_b);
		a.put((int)'B', StrftimeSimpleDateAction.ACTION_B);
		a.put((int)'c', new StrftimeClAction());
		a.put((int)'C', StrftimeSimpleDateAction.ACTION_y);
		a.put((int)'d', StrftimeSimpleDateAction.ACTION_d);
		a.put((int)'D', StrftimeSimpleDateAction.ACTION_D);
		a.put((int)'e', new StrftimeElAction());
		a.put((int)'E', new StrftimeEuAction());
		a.put((int)'f', new StrftimeFlAction());
		a.put((int)'F', new StrftimeFuAction());
		// g
		// G
		a.put((int)'h', StrftimeSimpleDateAction.ACTION_b);
		a.put((int)'H', StrftimeSimpleDateAction.ACTION_H);
		// i
		a.put((int)'I', StrftimeSimpleDateAction.ACTION_I);
		a.put((int)'j', new StrftimeJlAction());
		// J
		a.put((int)'k', new StrftimeKlAction());
		// K
		a.put((int)'l', new StrftimeLlAction());
		// L
		a.put((int)'m', StrftimeSimpleDateAction.ACTION_m);
		a.put((int)'M', StrftimeSimpleDateAction.ACTION_M);
		a.put((int)'n', new StrftimeNlAction());
		// N
		// o
		// O
		a.put((int)'p', StrftimeSimpleDateAction.ACTION_p);
		// P
		// q
		// Q
		a.put((int)'r', StrftimeSimpleDateAction.ACTION_r);
		a.put((int)'R', StrftimeSimpleDateAction.ACTION_R);
		// s
		a.put((int)'S', StrftimeSimpleDateAction.ACTION_S);
		a.put((int)'t', new StrftimeTlAction());
		a.put((int)'T', StrftimeSimpleDateAction.ACTION_T);
		a.put((int)'u', new StrftimeUlAction());
		a.put((int)'U', new StrftimeUuAction());
		a.put((int)'v', StrftimeSimpleDateUpperAction.ACTION_v);
		a.put((int)'V', StrftimeSimpleDateAction.ACTION_V);
		a.put((int)'w', new StrftimeWlAction());
		a.put((int)'W', new StrftimeWuAction());
		a.put((int)'x', new StrftimeXlAction());
		a.put((int)'X', new StrftimeXuAction());
		a.put((int)'y', StrftimeSimpleDateAction.ACTION_y);
		a.put((int)'Y', StrftimeSimpleDateAction.ACTION_Y);
		a.put((int)'z', StrftimeSimpleDateAction.ACTION_z);
		a.put((int)'Z', StrftimeSimpleDateAction.ACTION_Z);
		return new Strftime('%', a, -1);
	}

	/**
	 * 
	 * @param b
	 * @param format
	 * @param arg
	 * @throws IOException
	 */
	public void format(Writer b, String format,
			java.util.Date arg) throws IOException {
		StrftimeAction a;
		S1 s = S1.INIT;
		int c;

		for(int i = 0; i < format.length(); i += c > 0xffff ? 2 : 1) {
			c = format.charAt(i);
			switch(s) {
			case INIT:
				if(c == fchr) {
					s = S1.FLAG;
				} else {
					b.append((char)c);
				}
				break;
			case FLAG:
				if(c == fchr) {
					b.append((char)c);
				} else if((a = actions.get(c)) == null) {
					b.append((char)fchr).append((char)c);
				} else {
					a.action(b, arg);
				}
				s = S1.INIT;
				break;
			}
		}
	}

	/**
	 * 
	 * @param format
	 * @param arg
	 * @return
	 */
	public String format(String format, java.util.Date arg) {
		StringWriter b = new StringWriter();

		try {
			format(b, format, arg);
			return b.toString();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
