package org.nutz.lang.util;

import java.io.Serializable;
import java.util.ArrayList;

import org.nutz.json.Json;
import org.nutz.lang.Lang;

public class LinkedIntArray implements Serializable {

	private static final long serialVersionUID = 1L;

	public LinkedIntArray() {
        this(256);
    }

    public LinkedIntArray(int size) {
        if (size <= 0)
            Lang.makeThrow("width must >0!");
        this.width = size;
        cache = new ArrayList<int[]>();
    }

    private int offset;
    private int cursor;
    private int width;
    private ArrayList<int[]> cache;

    public LinkedIntArray push(int e) {
        int[] array;
        int row = cursor / width;
        int i = cursor % width;
        if (cache.size() == 0 || (cursor != offset && i == 0)) {
            array = new int[width];
            cache.add(array);
        } else {
            array = cache.get(row);
        }
        array[i] = e;
        cursor++;
        return this;
    }

    public int popFirst() {
        return innerGet(offset++);
    }

    public int popLast() {
        return innerGet(--cursor);
    }

    public LinkedIntArray popLast(int num) {
        for (int i = 0; i < num; i++)
            popLast();
        return this;
    }

    public int first() {
        if (size() == 0)
            return (char) 0;
        return innerGet(offset);
    }

    public int last() {
        if (size() == 0)
            return (char) 0;
        return innerGet(cursor - 1);
    }

    public LinkedIntArray set(int index, int e) {
        checkBound(index);
        index += offset;
        int[] array = cache.get(index / width);
        array[index % width] = e;
        return this;
    }

    public LinkedIntArray setLast(int e) {
        set(this.size() - 1, e);
        return this;
    }

    private void checkBound(int index) {
        if (index >= size() || index < 0)
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
    }

    public LinkedIntArray clear() {
        cache.clear();
        cursor = 0;
        offset = 0;
        return this;
    }

    private int innerGet(int index) {
        int[] array = cache.get(index / width);
        return array[index % width];
    }

    public int get(int index) {
        checkBound(index);
        return innerGet(index + offset);
    }

    public boolean isEmpty() {
        return 0 == cursor - offset;
    }

    public int size() {
        return cursor - offset;
    }

    public int[] toArray() {
        int[] re = new int[size()];
        for (int i = 0; i < re.length; i++)
            re[i] = this.get(i);
        return re;
    }

    public byte[] toByteArray() {
        byte[] re = new byte[size()];
        for (int i = 0; i < re.length; i++)
            re[i] = (byte) this.get(i);
        return re;
    }

    public String toString() {
        return Lang.concat(',', toArray()).toString();
    }

    public String toJson() {
        return Json.toJson(toArray());
    }
}
