package io.mycat.server;

import io.mycat.net.BufferArray;
import io.mycat.net.Connection;
import io.mycat.net.NetSystem;
import io.mycat.server.packet.AuthPacket;
import io.mycat.server.packet.ErrorPacket;
import io.mycat.server.packet.HandshakePacket;
import io.mycat.server.packet.util.CharsetUtil;
import io.mycat.server.packet.util.SecurityUtil;

import java.io.UnsupportedEncodingException;
import java.nio.channels.SocketChannel;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * MySQL Front connection
 * 
 * @author wuzhih
 * 
 */

public class GenalMySQLConnection extends Connection {
	public static final int maxPacketSize = 16 * 1024 * 1024;
	protected final AtomicBoolean isQuit = new AtomicBoolean(false);
	protected byte[] seed;
	protected String user;
	protected volatile String schema;
	protected volatile int txIsolation;
	protected volatile boolean autocommit;
	protected volatile boolean txInterrupted;
	protected volatile String txInterrputMsg = "";
	protected long lastInsertId;
	protected volatile String oldSchema;
	protected long clientFlags;
	protected String password;
	protected boolean isAccepted;
	protected boolean isAuthenticated;
	protected volatile String charset;
	protected volatile int charsetIndex;
	protected HandshakePacket handshake;
	protected boolean isSupportCompress = false;

	public GenalMySQLConnection(SocketChannel channel) {
		super(channel);
		this.txInterrupted = false;
		this.autocommit = true;
	}

	public int getTxIsolation() {
		return txIsolation;
	}

	public void setTxIsolation(int txIsolation) {
		this.txIsolation = txIsolation;
	}

	public boolean isAutocommit() {
		return autocommit;
	}

	public boolean isAuthenticated() {
		return isAuthenticated;
	}

	public boolean isSupportCompress() {
		return isSupportCompress;
	}

	public void setSupportCompress(boolean isSupportCompress) {
		this.isSupportCompress = isSupportCompress;
	}

	public void setAutocommit(boolean autocommit) {
		this.autocommit = autocommit;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public long getLastInsertId() {
		return lastInsertId;
	}

	public void setLastInsertId(long lastInsertId) {
		this.lastInsertId = lastInsertId;
	}

	public void setAuthenticated(boolean isAuthenticated) {
		this.isAuthenticated = isAuthenticated;
	}

	public void writeErrMessage(int errno, String msg) {
		ErrorPacket err = new ErrorPacket();
		err.packetId = 1;
		err.errno = errno;
		err.message = encodeString(msg, charset);
		err.write(this);
	}

	public String getUser() {
		return user;
	}

	public int getCharsetIndex() {
		return charsetIndex;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getSchema() {
		return schema;
	}

	public final static byte[] encodeString(String src, String charset) {
		if (src == null) {
			return null;
		}
		if (charset == null) {
			return src.getBytes();
		}
		try {
			return src.getBytes(charset);
		} catch (UnsupportedEncodingException e) {
			return src.getBytes();
		}
	}

	public void setSchema(String newSchema) {
		String curSchema = schema;
		if (curSchema == null) {
			this.schema = newSchema;
			this.oldSchema = newSchema;
		} else {
			this.oldSchema = curSchema;
			this.schema = newSchema;
		}
	}

	public byte[] getSeed() {
		return seed;
	}

	public boolean setCharsetIndex(int ci) {
		String charset = CharsetUtil.getCharset(ci);
		if (charset != null) {
			return setCharset(charset);
		} else {
			return false;
		}
	}

	public String getCharset() {
		return charset;
	}

	public boolean setCharset(String charset) {
		int ci = CharsetUtil.getIndex(charset);
		if (ci > 0) {
			this.charset = charset.equalsIgnoreCase("utf8mb4") ? "utf8"
					: charset;
			this.charsetIndex = ci;
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 设置是否需要中断当前事务
	 */
	public void setTxInterrupt(String txInterrputMsg) {
		if (!autocommit && !txInterrupted) {
			txInterrupted = true;
			this.txInterrputMsg = txInterrputMsg;
		}
	}

	public boolean isTxInterrupted() {
		return txInterrupted;
	}

	public HandshakePacket getHandshake() {
		return handshake;
	}

	public void setHandshake(HandshakePacket handshake) {
		this.handshake = handshake;
	}

	private static byte[] passwd(String pass, HandshakePacket hs)
			throws NoSuchAlgorithmException {
		if (pass == null || pass.length() == 0) {
			return null;
		}
		byte[] passwd = pass.getBytes();
		int sl1 = hs.seed.length;
		int sl2 = hs.restOfScrambleBuff.length;
		byte[] seed = new byte[sl1 + sl2];
		System.arraycopy(hs.seed, 0, seed, 0, sl1);
		System.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);
		return SecurityUtil.scramble411(passwd, seed);
	}

	public void authenticate() {
		AuthPacket packet = new AuthPacket();
		packet.packetId = 1;
		packet.clientFlags = clientFlags;
		packet.maxPacketSize = maxPacketSize;
		packet.charsetIndex = this.charsetIndex;
		packet.user = user;
		try {
			packet.password = passwd(password, handshake);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}
		packet.database = schema;
		BufferArray bufferArray = NetSystem.getInstance().getBufferPool()
				.allocateArray();
		packet.write(bufferArray);
		// write to connection
		this.write(bufferArray);

	}
}

@@ -3681,16 +3681,133 @@
 rset) %7B%0A
+%09%09%0A%09%09//%E4%BF%AE%E5%A4%8DPHP%E5%AD%97%E7%AC%A6%E9%9B%86%E8%AE%BE%E7%BD%AE%E9%94%99%E8%AF%AF, %E5%A6%82%EF%BC%9A set names 'utf8'%0A%09%09if ( charset != null ) %7B%09%09%09%0A%09%09%09charset = charset.replace(%22'%22, %22%22);%0A%09%09%7D%0A%09%09%0A
 %09%09int ci

修复PHP字符集设置错误, 如： set names 'utf8'