package cn.myapps.runtime.common.filter;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import cn.myapps.authtime.common.service.AuthTimeServiceManager;
import cn.myapps.base.web.WebUser;
import cn.myapps.common.util.StringUtil;
import cn.myapps.util.http.CookieUtil;
import cn.myapps.util.property.MultiLanguageProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.ContentCachingRequestWrapper;

public final class MultiLanguageFilter extends HttpServlet implements Filter {
	private static final Logger logger  = LoggerFactory.getLogger(MultiLanguageFilter.class);
	
	private static final long serialVersionUID = 4540909316344106991L;

	public void doFilter(ServletRequest rq, ServletResponse rs, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest hreq = (HttpServletRequest) rq;

		HttpServletResponse response = (HttpServletResponse) rs;
		response.setCharacterEncoding("UTF-8");
		MultiLanguageHttpServletResponse mResponse = new MultiLanguageHttpServletResponse(hreq, response);

		try {
			ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(hreq);
			chain.doFilter(wrappedRequest, mResponse);
			mResponse.setContentLength(mResponse.getSize());
			mResponse.flushBuffer();
		} catch (Exception e) {
			logger.error("doFilter(url={})参数发生异常", hreq.getRequestURI(), e);
		}
	}

	public void init(FilterConfig arg0) throws ServletException {
		
	}

}

class MultiLanguageHttpServletResponse extends HttpServletResponseWrapper {
	private ServletResponse inResp;

	private ServletRequest inRequ;

	private MultiLanguageServletOutputStream outStream;

	private MultiLanguagePrintWriter outWriter;

	public MultiLanguageHttpServletResponse(ServletRequest inRequ, ServletResponse inResp) throws java.io.IOException {
		super((HttpServletResponse) inResp);
		inResp.setCharacterEncoding("UTF-8");
		this.inResp = inResp;
		this.inRequ = inRequ;
	}

	public ServletOutputStream getOutputStream() throws java.io.IOException {
		outStream = new MultiLanguageServletOutputStream(inRequ, inResp);
		return outStream;
	}

	public PrintWriter getWriter() throws java.io.IOException {
		outWriter = new MultiLanguagePrintWriter(inRequ, inResp);
		return outWriter;
	}

	public int getSize() {
		int count = 0;
		if (outStream != null) {
			count += outStream.getSize();
		}

		if (outWriter != null) {
			count += outWriter.getSize();
		}

		return count;
	}
}

class MultiLanguageServletOutputStream extends ServletOutputStream {
	int flag;

	int count;

	int[] buf;

	int size;
	
	String domain;

	String application;

	String language;

	OutputStream outStream;

	HttpServletRequest request;

	public MultiLanguageServletOutputStream(ServletRequest inRequ, ServletResponse inResp) throws IOException {
		inResp.setCharacterEncoding("UTF-8");
		this.outStream = inResp.getOutputStream();
		buf = new int[512];
		HttpServletRequest request = ((HttpServletRequest) inRequ);
		//HttpSession session = request.getSession();
		if(request.getRequestURI().indexOf("/logout") > 0){
			return;
		}
		WebUser webUser = null;
		if(request.getRequestURI().indexOf("dwr") < 0){
			webUser = AuthTimeServiceManager.getWebUser(request);
		}

		language = CookieUtil.getMultiLanguage(request);
		if (StringUtil.isBlank(language)) {
			Locale loc = request.getLocale();
			if (loc.equals(Locale.CHINA) || loc.equals(Locale.PRC) || loc.toString().contains("CN"))
				language = "CN";
			else if (loc.equals(Locale.TAIWAN))
				language = "TW";
			else {
				language = loc.getLanguage().toUpperCase();
				if (MultiLanguageProperty.getType(language) == 0) {
					language = "EN";
				}
			}
		}

		if (webUser != null) {
			domain = webUser.getDomainid();
			application = webUser.getDefaultApplication();
		} else {
			domain = null;
			application = null;
		}
	}

	private void writeToBuf(int b) {
		int newcount = count + 1;
		if (newcount > buf.length) {
			int newbuf[] = new int[Math.max(buf.length << 1, newcount)];
			System.arraycopy(buf, 0, newbuf, 0, count);
			buf = newbuf;
		}
		buf[count] = b;
		count = newcount;

	}

	private void clearBuf() throws IOException {
		for (int i = 0; i < count; i++) {
			outStream.write(buf[i]);
			size++;
		}
		count = 0;
		flag = 0;
	}

	public void write(int i) throws java.io.IOException {
		switch (i) {
		case '{':
			if (flag == 0) {
				flag = 1;
				writeToBuf(i);
			} else {
				clearBuf();
				outStream.write(i);
				size++;
			}

			break;
		case '*':
			if (flag == 1) {
				flag = 2;
				writeToBuf(i);
			} else if (flag == 4) {
				flag = 5;
				writeToBuf(i);
			} else {
				clearBuf();
				outStream.write(i);
				size++;
			}

			break;
		case '[':
			if (flag == 2) {
				flag = 3;
				writeToBuf(i);
			} else {
				clearBuf();
				outStream.write(i);
				size++;
			}

			break;
		case ']':
			if (flag == 3) {
				flag = 4;
				writeToBuf(i);
			} else {
				clearBuf();
				outStream.write(i);
				size++;
			}

			break;
		case '}':
			if (flag == 5) {
				writeToBuf(i);
				// start to replace
				String origText = StringUtil.toString(buf, 3, count - 6);
				String newText;
				try {
					newText =  MultiLanguageProperty.replaceText(application, language, origText);
					byte[] data = newText.getBytes("UTF-8");
					outStream.write(data);
					size += data.length;
				} catch (Exception e) {
					e.printStackTrace();
				}
				count = 0;
				flag = 0;
			} else {
				clearBuf();
				outStream.write(i);
				size++;
			}

			break;

		default:
			if (flag == 3) {
				writeToBuf(i);
			} else {
				if (count > 0)
					clearBuf();
				outStream.write(i);
				size++;
			}

			break;

		}
	}

	public void close() throws java.io.IOException {
		if (count > 0)
			clearBuf();
		outStream.close();

	}

	public void flush() throws java.io.IOException {
		if (count > 0)
			clearBuf();
		outStream.flush();
	}

	/**
	 * @return the size
	 * @uml.property name="size"
	 */
	public int getSize() {
		return size;
	}

	@Override
	public boolean isReady() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void setWriteListener(WriteListener writeListener) {
		// TODO Auto-generated method stub
	}

}

class MultiLanguagePrintWriter extends PrintWriter {
	int flag;

	int count;

	int[] buf;

	String domain;

	String application;

	String language;

	int size;

	HttpServletRequest request;

	public MultiLanguagePrintWriter(ServletRequest inRequ, ServletResponse inResp) throws IOException {

		this(inResp.getWriter(), false);
		this.out = inResp.getWriter();
		inResp.setCharacterEncoding("UTF-8");

		HttpServletRequest request = ((HttpServletRequest) inRequ);
//		HttpSession session = request.getSession();
		WebUser webUser = null;
		if(request.getRequestURI().indexOf("dwr") < 0){
			webUser = AuthTimeServiceManager.getWebUser(request);
		}

		language = CookieUtil.getMultiLanguage(request);
		if (StringUtil.isBlank(language)) {
			Locale loc = request.getLocale();
			if (loc.equals(Locale.CHINA) || loc.equals(Locale.PRC))
				language = "CN";
			else if (loc.equals(Locale.TAIWAN))
				language = "TW";
			else {
				language = loc.getLanguage().toUpperCase();
				if (MultiLanguageProperty.getType(language) == 0) {
					language = "EN";
				}
			}
		}

		if (webUser != null) {
			application = webUser.getDefaultApplication();
			domain = webUser.getDomainid();
		} else {
			application = null;
			domain = null;
		}
	}

	public MultiLanguagePrintWriter(Writer out, boolean autoFlush) {
		super(out);
		buf = new int[512];
	}

	public void flush() {
		if (count > 0)
			clearBuf();
		super.flush();
	}

	public void close() {
		if (count > 0)
			clearBuf();
		super.close();
	}

	public void write(char buf2[], int off, int len) {
		for (int i = off; i < Math.min(off + len, buf2.length); i++) {
			this.write((int) buf2[i]);
		}
	}

	public void write(String s) {
		write(s.toCharArray(), 0, s.length());
	}

	private void writeToBuf(int b) {
		int newcount = count + 1;
		if (newcount > buf.length) {
			int newbuf[] = new int[Math.max(buf.length << 1, newcount)];
			System.arraycopy(buf, 0, newbuf, 0, count);
			buf = newbuf;
		}
		buf[count] = b;
		count = newcount;
	}

	public void write(int c) {
		switch (c) {
		case '{':
			if (flag == 0) {
				flag = 1;
				writeToBuf(c);
			} else {
				clearBuf();
				super.write(c);
				size++;
			}

			break;
		case '*':
			if (flag == 1) {
				flag = 2;
				writeToBuf(c);
			} else if (flag == 4) {
				flag = 5;
				writeToBuf(c);
			} else {
				clearBuf();
				super.write(c);
				size++;
			}

			break;
		case '[':
			if (flag == 2) {
				flag = 3;
				writeToBuf(c);
			} else {
				clearBuf();
				super.write(c);
				size++;
			}

			break;
		case ']':
			if (flag == 3) {
				flag = 4;
				writeToBuf(c);
			} else {
				clearBuf();
				super.write(c);
				size++;
			}

			break;
		case '}':
			if (flag == 5) {
				writeToBuf(c);
				// start to replace
				String origText = StringUtil.toString(buf, 3, count - 6);
				String newText = MultiLanguageProperty.replaceText(application, language, origText);
				try {
					super.write(newText);
					size += newText.getBytes("UTF-8").length;
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				count = 0;
				flag = 0;
			} else {
				clearBuf();
				super.write(c);
				size++;
			}

			break;

		default:
			if (flag == 3) {
				writeToBuf(c);
			} else {
				if (count > 0)
					clearBuf();
				super.write(c);
				size++;
			}

			break;
		}
	}

	private void clearBuf() {
		for (int i = 0; i < count; i++) {
			super.write(buf[i]);
			size++;
		}
		count = 0;
		flag = 0;
	}

	public int getSize() {
		return size;
	}

}
