/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/
 */
package net.ranides.assira.text.format;

import java.awt.Color;
import java.awt.Font;
import java.util.Arrays;
import net.ranides.assira.math.Bitwise;

/**
 *
 * @author ranides
 */
public class AnsiString extends AnsiCharSequence {

    private static final long serialVersionUID = 0x202L;

    protected enum Init { DIRECT };
    
    protected char[] text;
    protected byte[] foreList; 
    protected byte[] backList; 
    protected char[] flagList; 
    protected byte[] fontList; 
    
    protected AnsiScheme scheme = AnsiScheme.DEFAULT;

    public AnsiString(AnsiCharSequence sequence) {
        this(sequence, 0, sequence.length());
    }
    
    public AnsiString(String text) {
        this(Init.DIRECT, text.toCharArray());
    }
    
    public AnsiString(AnsiCharSequence source, int begin, int end) {
        if (source instanceof AnsiString) {
            AnsiString str  = (AnsiString)source;
            this.scheme     = str.scheme;
            this.text       = Arrays.copyOfRange(str.text, begin, end);
            this.foreList   = Arrays.copyOfRange(str.foreList, begin, end);
            this.backList   = Arrays.copyOfRange(str.backList, begin, end);
            this.flagList   = Arrays.copyOfRange(str.flagList, begin, end);
            this.fontList   = Arrays.copyOfRange(str.fontList, begin, end);
        } else {
            int count       = end-begin;
            this.scheme     = source.getScheme();
            this.text       = new char[count];
            this.foreList   = new byte[count];
            this.backList   = new byte[count];
            this.flagList   = new char[count];
            this.fontList   = new byte[count];
            for(int i=0; i<count; i++) {
                int offset = i+begin;
                this.text[i]        = source.charAt(offset);
                this.foreList[i]    = (byte)source.getForeAt(offset);
                this.backList[i]    = (byte)source.getBackAt(offset);
                this.flagList[i]    = (char)source.getFlagsAt(offset);
                this.fontList[i]    = (byte)source.getFontAt(offset);
            }
        }
    }
    
    @SuppressWarnings("PMD")
    protected AnsiString(Init _i, char[] text) {
        this.text       = text;
        this.foreList   = new byte[ text.length ];
        this.backList   = new byte[ text.length ];
        this.flagList   = new char[ text.length ];
        this.fontList   = new byte[ text.length ];
    }
    
    protected AnsiString(char[] text, byte[] backList, byte[] foreList, char[] flagList, byte[] fontList, int size) {
        this.text       = Arrays.copyOf(text, size);
        this.foreList   = Arrays.copyOf(foreList, size);
        this.backList   = Arrays.copyOf(backList, size);
        this.flagList   = Arrays.copyOf(flagList, size);
        this.fontList   = Arrays.copyOf(fontList, size);
    }
    
    protected AnsiString(int capacity) {
        this.text       = new char[capacity];
        this.foreList   = new byte[capacity];
        this.backList   = new byte[capacity];
        this.flagList   = new char[capacity];
        this.fontList   = new byte[capacity];
    }

    @Override
    public AnsiScheme getScheme() {
        return scheme;
    }
    
    @Override
    public Color getComputedForeAt(int index) {
        return scheme.getColor(getForeAt(index));
    }

    @Override
    public Color getComputedBackAt(int index) {
        return scheme.getColor(getBackAt(index));
    }
    
    @Override
    public Font getComputedFontAt(int index) {
        return scheme.getFont(getFontAt(index));
    }
    
    @Override
    public int getForeAt(int index) {
        return foreList[index] & 0xFF;
    }
    
    @Override
    public int getBackAt(int index) {
        return backList[index] & 0xFF;
    }
    
    @Override
    public int getFlagsAt(int index) {
        return flagList[index];
    }

    @Override
    public int getFontAt(int index) {
        return fontList[index] & 0xFF;
    }

    @Override
    public int length() {
        return text.length;
    }

    @Override
    public char getCharAt(int index) {
        return text[index];
    }
    
    @Override
    public void setScheme(AnsiScheme scheme) {
        this.scheme = scheme;
    }
    
    @Override
    public void setCharAt(int index, char value) {
        text[index] = value;
    }

    @Override
    public void setBackAt(int index, int color) {
        backList[index] = (byte)color;
    }
    
    @Override
    public void setForeAt(int index, int color) {
        foreList[index] = (byte)color;
    }
    
    @Override
    public void setFontAt(int index, int font) {
        fontList[index] = (byte)font;
    }
    
    @Override
    public void setFlagsAt(int index, int flags) {
        flagList[index] &= ~((flags & AnsiAttr.MASK_FLAGS_CLR) >> 8);
        flagList[index] |= (flags & AnsiAttr.MASK_FLAGS_SET);
    }
    
        
    @Override
    public void setBack(int from, int to, int attr) {
        byte value = (byte)attr;
        for(int i=from; i<to && i<backList.length; i++) { backList[i] = value; }
    }
    
    @Override
    public void setFore(int from, int to, int attr) {
        byte value = (byte)attr;
        for(int i=from; i<to && i<foreList.length; i++) { foreList[i] = value; }
    }
    
    @Override
    public void setFont(int from, int to, int font) {
        byte value = (byte)font;
        for (int i = from; i < to && i < fontList.length; i++) {
            fontList[i] = value;
        }
    }
    
    @Override
    public void setFlags(int from, int to, int flags) {
        char clrv = (char)~(char)((flags & AnsiAttr.MASK_FLAGS_CLR) >> 8);
        char setv = (char)(flags & AnsiAttr.MASK_FLAGS_SET);
        for (int i = from; i < to && i < flagList.length; i++) {
            flagList[i] &= clrv;
            flagList[i] |= setv;
        }
    }
    
    @SuppressWarnings({
        "PMD.NPathComplexity", 
        "PMD.CyclomaticComplexity"
    })
    @Override
    public boolean equals(AnsiCharSequence value, int flags) {
        if( this == value ) {
            return true;
        }
        if( value.length() != length() ) {
            return false;
        }
        if(value instanceof AnsiString) {
            AnsiString str = (AnsiString)value;
            if( Bitwise.has(flags, AnsiAttr.CMP_CHAR) && !Arrays.equals(this.text, str.text)) {
                return false;
            }
            if( Bitwise.has(flags, AnsiAttr.CMP_FORE) && !Arrays.equals(this.foreList, str.foreList)) {
                return false;
            }
            if( Bitwise.has(flags, AnsiAttr.CMP_BACK) && !Arrays.equals(this.backList, str.backList)) {
                return false;
            }
            if( Bitwise.has(flags, AnsiAttr.CMP_FONT) && !Arrays.equals(this.fontList, str.fontList)) {
                return false;
            }
            if( Bitwise.has(flags, AnsiAttr.CMP_FLAGS_ALL) && !Arrays.equals(this.flagList, str.flagList)) {
                return false;
            }
            return super.equals(value, flags & AnsiAttr.CMP_FLAGS_ALL);
        }
        return super.equals(value, flags);
    }
  
}
