import Texture from '../Texture';
import Texture2D from '../Texture2D';
import TextureCube from '../TextureCube';

// http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx
// https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
var DDS_MAGIC = 0x20534444;

var DDSD_CAPS = 0x1;
var DDSD_HEIGHT = 0x2;
var DDSD_WIDTH = 0x4;
var DDSD_PITCH = 0x8;
var DDSD_PIXELFORMAT = 0x1000;
var DDSD_MIPMAPCOUNT = 0x20000;
var DDSD_LINEARSIZE = 0x80000;
var DDSD_DEPTH = 0x800000;

var DDSCAPS_COMPLEX = 0x8;
var DDSCAPS_MIPMAP = 0x400000;
var DDSCAPS_TEXTURE = 0x1000;

var DDSCAPS2_CUBEMAP = 0x200;
var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400;
var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800;
var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000;
var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000;
var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000;
var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000;
var DDSCAPS2_VOLUME = 0x200000;

var DDPF_ALPHAPIXELS = 0x1;
var DDPF_ALPHA = 0x2;
var DDPF_FOURCC = 0x4;
var DDPF_RGB = 0x40;
var DDPF_YUV = 0x200;
var DDPF_LUMINANCE = 0x20000;

function fourCCToInt32(value) {
    return value.charCodeAt(0) +
        (value.charCodeAt(1) << 8) +
        (value.charCodeAt(2) << 16) +
        (value.charCodeAt(3) << 24);
}

function int32ToFourCC(value) {
    return String.fromCharCode(
        value & 0xff,
        (value >> 8) & 0xff,
        (value >> 16) & 0xff,
        (value >> 24) & 0xff
    );
}

var headerLengthInt = 31; // The header length in 32 bit ints

var FOURCC_DXT1 = fourCCToInt32('DXT1');
var FOURCC_DXT3 = fourCCToInt32('DXT3');
var FOURCC_DXT5 = fourCCToInt32('DXT5');
// Offsets into the header array
var off_magic = 0;

var off_size = 1;
var off_flags = 2;
var off_height = 3;
var off_width = 4;

var off_mipmapCount = 7;

var off_pfFlags = 20;
var off_pfFourCC = 21;

var off_caps = 27;
var off_caps2 = 28;
var off_caps3 = 29;
var off_caps4 = 30;

var ret = {
    parse: function(arrayBuffer, out) {
        var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
        if (header[off_magic] !== DDS_MAGIC) {
            return null;
        }
        if (!header(off_pfFlags) & DDPF_FOURCC) {
            return null;
        }

        var fourCC = header(off_pfFourCC);
        var width = header[off_width];
        var height = header[off_height];
        var isCubeMap = header[off_caps2] & DDSCAPS2_CUBEMAP;
        var hasMipmap = header[off_flags] & DDSD_MIPMAPCOUNT;
        var blockBytes, internalFormat;
        switch(fourCC) {
            case FOURCC_DXT1:
                blockBytes = 8;
                internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT;
                break;
            case FOURCC_DXT3:
                blockBytes = 16;
                internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT;
                break;
            case FOURCC_DXT5:
                blockBytes = 16;
                internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT;
                break;
            default:
                return null;
        }
        var dataOffset = header[off_size] + 4;
        // TODO: Suppose all face are existed
        var faceNumber = isCubeMap ? 6 : 1;
        var mipmapCount = 1;
        if (hasMipmap) {
            mipmapCount = Math.max(1, header[off_mipmapCount]);
        }

        var textures = [];
        for (var f = 0; f < faceNumber; f++) {
            var _width = width;
            var _height = height;
            textures[f] = new Texture2D({
                width: _width,
                height: _height,
                format: internalFormat
            });
            var mipmaps = [];
            for (var i = 0; i < mipmapCount; i++) {
                var dataLength = Math.max(4, _width) / 4 * Math.max(4, _height) / 4 * blockBytes;
                var byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);

                dataOffset += dataLength;
                _width *= 0.5;
                _height *= 0.5;
                mipmaps[i] = byteArray;
            }
            textures[f].pixels = mipmaps[0];
            if (hasMipmap) {
                textures[f].mipmaps = mipmaps;
            }
        }
        // TODO
        // return isCubeMap ? textures : textures[0];
        if (out) {
            out.width = textures[0].width;
            out.height = textures[0].height;
            out.format = textures[0].format;
            out.pixels = textures[0].pixels;
            out.mipmaps = textures[0].mipmaps;
        }
        else {
            return textures[0];
        }
    }
};

export default ret;
