package com.dianping.cat.message.internal;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import org.unidal.helper.Splitters;

import com.dianping.cat.configuration.NetworkInterfaceManager;

public class MessageIdFactory {
	private volatile long m_timestamp = getTimestamp();

	private volatile AtomicInteger m_index;

	private String m_domain;

	private String m_ipAddress;

	private volatile boolean m_initialized;

	private MappedByteBuffer m_byteBuffer;

	private RandomAccessFile m_markFile;

	private static final long HOUR = 3600 * 1000L;

	private BlockingQueue<String> m_reusedIds = new LinkedBlockingQueue<String>(100000);

	public void close() {
		try {
			m_markFile.close();
		} catch (Exception e) {
			// ignore it
		}
	}

	private File createMarkFile(String domain) {
		File mark = new File("/data/appdatas/cat/", "cat-" + domain + ".mark");

		if (!mark.exists()) {
			boolean success = true;
			try {
				success = mark.createNewFile();
			} catch (Exception e) {
				success = false;
			}
			if (!success) {
				mark = createTempFile(domain);
			}
		} else if (!mark.canWrite()) {
			mark = createTempFile(domain);
		}
		return mark;
	}

	private File createTempFile(String domain) {
		String tmpDir = System.getProperty("java.io.tmpdir");
		File mark = new File(tmpDir, "cat-" + domain + ".mark");

		return mark;
	}

	public String getNextId() {
		String id = m_reusedIds.poll();

		if (id != null) {
			return id;
		} else {
			long timestamp = getTimestamp();

			if (timestamp != m_timestamp) {
				m_index = new AtomicInteger(0);
				m_timestamp = timestamp;
			}

			int index = m_index.getAndIncrement();

			StringBuilder sb = new StringBuilder(m_domain.length() + 32);

			sb.append(m_domain);
			sb.append('-');
			sb.append(m_ipAddress);
			sb.append('-');
			sb.append(timestamp);
			sb.append('-');
			sb.append(index);

			return sb.toString();
		}
	}

	protected long getTimestamp() {
		long timestamp = MilliSecondTimer.currentTimeMillis();

		return timestamp / HOUR; // version 2
	}

	public void initialize(String domain) throws IOException {
		m_domain = domain;

		if (m_ipAddress == null) {
			String ip = NetworkInterfaceManager.INSTANCE.getLocalHostAddress();
			List<String> items = Splitters.by(".").noEmptyItem().split(ip);
			byte[] bytes = new byte[4];

			for (int i = 0; i < 4; i++) {
				bytes[i] = (byte) Integer.parseInt(items.get(i));
			}

			StringBuilder sb = new StringBuilder(bytes.length / 2);

			for (byte b : bytes) {
				sb.append(Integer.toHexString((b >> 4) & 0x0F));
				sb.append(Integer.toHexString(b & 0x0F));
			}

			m_ipAddress = sb.toString();
		}
		File mark = createMarkFile(domain);

		m_markFile = new RandomAccessFile(mark, "rw");
		m_byteBuffer = m_markFile.getChannel().map(MapMode.READ_WRITE, 0, 20);

		if (m_byteBuffer.limit() > 0) {
			int index = m_byteBuffer.getInt();
			long lastTimestamp = m_byteBuffer.getLong();

			if (lastTimestamp == m_timestamp) { // for same hour
				m_index = new AtomicInteger(index + 10000);
			} else {
				m_index = new AtomicInteger(0);
			}
		}

		saveMark();
	}

	protected void resetIndex() {
		m_index.set(0);
	}

	public void reuse(String id) {
		m_reusedIds.offer(id);
	}

	public void saveMark() {
		try {
			m_byteBuffer.rewind();
			m_byteBuffer.putInt(m_index.get());
			m_byteBuffer.putLong(m_timestamp);
		} catch (Exception e) {
			// ignore it
		}
	}

	public void setDomain(String domain) {
		m_domain = domain;
	}

	public void setIpAddress(String ipAddress) {
		m_ipAddress = ipAddress;
	}


	public synchronized boolean isInitialized() {
		return m_initialized;
	}

	public synchronized void setInit(boolean initialized) {
		m_initialized = initialized;
	}
}

@@ -2357,16 +2357,43 @@
 ption %7B%0A
+%09    if (!m_initialized) %7B%0A
 %09%09m_doma
@@ -3369,19 +3369,61 @@
 %09%09%7D%0A%09%09%7D%0A
-%0A
 %09%09
+%0A%09%09m_initialized = true;%0A%09    %7D%0A%09    %0A%09    
 saveMark
@@ -3566,32 +3566,56 @@
 aveMark() %7B%0A
+%09%09if (m_initialized) %7B%0A%09
 %09%09try %7B%0A
 %09%09%09m_byteBuf
@@ -3598,24 +3598,25 @@
  %7B%0A%09%09%09try %7B%0A
+%09
 %09%09%09m_byteBuf
@@ -3629,16 +3629,17 @@
 wind();%0A
+%09
 %09%09%09m_byt
@@ -3672,16 +3672,17 @@
 ());%0A%09%09%09
+%09
 m_byteBu
@@ -3708,16 +3708,17 @@
 stamp);%0A
+%09
 %09%09%7D catc
@@ -3730,32 +3730,33 @@
 ception e) %7B%0A%09%09%09
+%09
 // ignore it%0A%09%09%7D
@@ -3744,32 +3744,37 @@
 %09%09%09// ignore it%0A
+%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%0A%09public 
@@ -3910,174 +3910,6 @@
 %0A%09%7D%0A
-%0A%0A%09public synchronized boolean isInitialized() %7B%0A%09%09return m_initialized;%0A%09%7D%0A%0A%09public synchronized void setInit(boolean initialized) %7B%0A%09%09m_initialized = initialized;%0A%09%7D%0A
 %7D%0A

Update MessageIdFactory.java