/*
 * Copyright 2017 JOOTNET Project
 * 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.
 * 
 * Support: https://github.com/jootnet/mir2.core
 */
package com.github.jootnet.mir2.core.image;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.InflaterInputStream;

import com.github.jootnet.mir2.core.BinaryReader;
import com.github.jootnet.mir2.core.SDK;
import com.github.jootnet.mir2.core.Texture;

/**
 * 热血传奇2WZL图片库
 * 
 * @author 云中双月
 */
final class WZL implements ImageLibrary {

	private int imageCount;
	/**
	 * 获取库中图片数量
	 * 
	 * @return 存在于当前WZL库中的图片数量
	 */
	int getImageCount() {
		return imageCount;
	}
    /* 图片数据起始位置 */
    private long[] offsetList;
    /* 图片数据长度 */
    private int[] lengthList;
    private ImageInfo[] imageInfos;
    /**
     * 获取库中图片信息数组
     * 
     * @return 所有存在于当前WZL库中的图片信息数组
     */
	ImageInfo[] getImageInfos() {
		return imageInfos;
	}
	/* WZL文件随机读取对象 */
	private BinaryReader br_wzl;
	private boolean loaded;
	/**
	 * 获取库加载状态
	 * 
	 * @return true表示库加载成功 false表示加载失败
	 */
	public boolean isLoaded() {
		return loaded;
	}
	/* 文件指针读取锁 */
    private Object wzl_locker = new Object();
    
    WZL(String wzlPath) {
    	String wzxPath = SDK.changeFileExtension(wzlPath, "wzx");
		File f_wzx = new File(wzxPath);
		if(!f_wzx.exists()) return;
		if(!f_wzx.isFile()) return;
		if(!f_wzx.canRead()) return;
		File f_wzl = new File(wzlPath);
		if(!f_wzl.exists()) return;
		if(!f_wzl.isFile()) return;
		if(!f_wzl.canRead()) return;
    	try {
    		BinaryReader br_wzx = new BinaryReader(f_wzx);
    		br_wzx.skipBytes(44); // 跳过标题
    		imageCount = br_wzx.readIntLE();
			offsetList = new long[imageCount];
			for (int i = 0; i < imageCount; ++i)
			{
				// 读取数据偏移地址
				offsetList[i] = br_wzx.readUnsignedIntLE();
			}
			br_wzx.close();
			br_wzl = new BinaryReader(f_wzl);
			imageInfos = new ImageInfo[imageCount];
            lengthList = new int[imageCount];
            for (int i = 0; i < imageCount; ++i) {
            	long offset = offsetList[i];
            	if(offset <= 48) {
            		// WZL里offset小于64的是空图片
					imageInfos[i] = ImageInfo.EMPTY;
            		continue;
            	}
            	if(offset + 16 > br_wzl.length()) {
					// 数据出错,直接赋值为空图片
					imageInfos[i] = ImageInfo.EMPTY;
            		continue;
				}
                // 读取图片信息和数据长度
                ImageInfo ii = new ImageInfo();
                br_wzl.seek(offset);
                ii.setColorBit((byte) (br_wzl.readByte() == 5 ? 16 : 8));
                ii.wzlCompressed = br_wzl.readByte() != 0;
                br_wzl.skipBytes(2); // 跳过2字节未知数据
                ii.setWidth(br_wzl.readUnsignedShortLE());
				ii.setHeight(br_wzl.readUnsignedShortLE());
				ii.setOffsetX(br_wzl.readShortLE());
				ii.setOffsetY(br_wzl.readShortLE());
                imageInfos[i] = ii;
                lengthList[i] = (int) br_wzl.readUnsignedIntLE();
            }
            loaded = true;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
    }

    /** 从zlib解压 
     * @throws IOException */
	private static byte[] unzip(byte[] ziped) throws IOException {
		InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(ziped));
		ByteArrayOutputStream o = new ByteArrayOutputStream(1024);
		int i = 1024;
		byte[] buf = new byte[i];

		while ((i = iis.read(buf, 0, i)) > 0) {
			o.write(buf, 0, i);
		}
		return o.toByteArray();
	}
    
    /**
     * 关闭WZL对象,释放其引用的文件流以及内存占用
     */
	public final void close() throws IOException {
		synchronized (wzl_locker) {
			offsetList = null;
			lengthList = null;
            imageInfos = null;
            loaded = false;
			if (br_wzl != null)
            {
				br_wzl.close();
            }
		}
	}

	public final Texture tex(int index) {
		if(!loaded) return Texture.EMPTY;
		if(index < 0) return Texture.EMPTY;
		if(index >= imageCount) return Texture.EMPTY;
		if(imageInfos[index] == ImageInfo.EMPTY) return Texture.EMPTY;
		if(lengthList[index] == 0) return Texture.EMPTY;
    	try{
    		ImageInfo ii = imageInfos[index];
    		long offset = offsetList[index];
    		int length = lengthList[index];
    		byte[] pixels = new byte[length];
    		synchronized (wzl_locker) {
        		br_wzl.seek(offset + 16);
        		br_wzl.read(pixels);
			}
    		if(ii.wzlCompressed)
    			pixels = unzip(pixels);
    		byte[] sRGB = new byte[ii.getWidth() * ii.getHeight() * 3];
    		if (ii.getColorBit() == 8) {
                int p_index = 0;
                for (int h = ii.getHeight() - 1; h >= 0; --h)
                    for (int w = 0; w < ii.getWidth(); ++w) {
                        // 跳过填充字节
                        if (w == 0)
                            p_index += SDK.skipBytes(8, ii.getWidth());
                        byte[] pallete = SDK.palletes[pixels[p_index++] & 0xff];
    					int _idx = (w + h * ii.getWidth()) * 3;
    					sRGB[_idx] = pallete[1];
    					sRGB[_idx + 1] = pallete[2];
    					sRGB[_idx + 2] = pallete[3];
                    }
            }
	    	else if (ii.getColorBit() == 16) {
	    		ByteBuffer bb = ByteBuffer.wrap(pixels);
	    		bb.order(ByteOrder.LITTLE_ENDIAN);
	    		int p_index = 0;
                for (int h = ii.getHeight() - 1; h >= 0; --h)
                    for (int w = 0; w < ii.getWidth(); ++w, p_index += 2) {
                        // 跳过填充字节
                        if (w == 0)
                            p_index += SDK.skipBytes(16, ii.getWidth());
                        short pdata = bb.getShort(p_index);
                        byte r = (byte) ((pdata & 0xf800) >> 8);// 由于是与16位做与操作,所以多出了后面8位
                        byte g = (byte) ((pdata & 0x7e0) >> 3);// 多出了3位,在强转时前8位会自动丢失
                        byte b = (byte) ((pdata & 0x1f) << 3);// 少了3位
    					int _idx = (w + h * ii.getWidth()) * 3;
    					sRGB[_idx] = r;
    					sRGB[_idx + 1] = g;
    					sRGB[_idx + 2] = b;
                    }
            }
	    	return new Texture(sRGB, ii.getWidth(), ii.getHeight());
    	} catch(Exception ex) {
    		ex.printStackTrace();
    		return Texture.EMPTY;
    	}
    }

	public final ImageInfo info(int index) {
		if(!loaded) return ImageInfo.EMPTY;
		if(index < 0) return ImageInfo.EMPTY;
		if(index >= imageCount) return ImageInfo.EMPTY;
		return imageInfos[index];
	}

	public int count() {
		return imageCount;
	}

}