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

import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
import net.ranides.assira.generic.HashUtils;
import net.ranides.assira.math.Bitwise;


/**
 *
 * @author ranides
 */
public abstract class AnsiAttr implements Serializable {
    
    private static final long serialVersionUID = 3L;
    
    public static final AnsiAttr DEFAULT = new Value(0, 0, 0, 0);
    
    public static final int NORMAL          = 0x0000;
    
    public static final int BOLD            = 0x0001;
    
    public static final int UNDERLINE       = 0x0002;
    
    public static final int INVERT          = 0x0004;
    
    public static final int SYMBOL          = 0x0008;
    
    public static final int NARROW          = 0x0010;
    
    public static final int STRIKE          = 0x0020;
    
    public static final int FRESH           = 0x0040;
    
    public static final int CHANGED         = 0x0080;
    
    public static final int NO_BOLD         = 0x0100;
    
    public static final int NO_UNDERLINE    = 0x0200;
    
    public static final int NO_INVERT       = 0x0400;
    
    public static final int NO_SYMBOL       = 0x0800;
    
    public static final int NO_NARROW       = 0x1000;
    
    public static final int NO_STRIKE       = 0x2000;
    
    public static final int NO_FRESH        = 0x4000;
    
    public static final int NO_CHANGED      = 0x8000;
    
    
    public static final int MASK_FLAGS_SET  = 0x00FF;
    
    public static final int MASK_FLAGS_CLR  = 0xFF00;
    
    
    public static final int CMP_FORE        = 0x010000;
    
    public static final int CMP_BACK        = 0x020000;
    
    public static final int CMP_FLAGS       = 0x040000;
    
    public static final int CMP_FONT        = 0x080000;
    
    public static final int CMP_CHAR        = 0x100000;
    
    public static final int CMP_FLAGS_ALL   = CMP_FLAGS | MASK_FLAGS_SET;
    
    public static final int CMP_FLAGS_PUBLIC= CMP_FLAGS | 0x003F;
    
    public static final int CMP_PUBLIC      = CMP_CHAR | CMP_FORE | CMP_BACK | CMP_FONT | CMP_FLAGS_PUBLIC;
    
    public static final int CMP_ALL         = CMP_PUBLIC | CMP_FLAGS_ALL;
    
    public static final int CMP_IDENTICAL   = 0xFFFFFFFF;    

    
    @Override
    public final boolean equals(Object object) {
        if(!(object instanceof AnsiAttr)) {
            return false;
        }
        return equals((AnsiAttr)object, AnsiAttr.CMP_PUBLIC);
    }
    
    public final boolean equals(AnsiAttr value, int flags) {
        if(this == value) {
            return true;
        }
        if (Bitwise.has(flags, AnsiAttr.CMP_FORE) && getFore() != value.getFore()) {
            return false;
        }
        if (Bitwise.has(flags, AnsiAttr.CMP_BACK) && getBack() != value.getBack()) {
            return false;
        }
        if (Bitwise.has(flags, AnsiAttr.CMP_FONT) && getFont() != value.getFont()) {
            return false;
        }
        if ((getFlags() & flags) != (value.getFlags() & flags)) {
            return false;
        }
        return true;
    }

    @Override
    public final int hashCode() {
        return HashUtils.hashValues(7, 19, getFore(), getBack(), getFlags() & AnsiAttr.CMP_PUBLIC);
    }
    
    @Override
    public final String toString() {
        return String.format("AnsiAttr{%d %d %d %s}", getBack(), getFore(), getFont(), AnsiStrings.encodeFlags(getFlags()));
    }
    
    /**
     * Zwraca przypisany numer koloru tekstu. Numer może być zamieniony na
     * graficzną reprezentację za pomocą {@link AnsiColorScheme}.
     * @see #getComputedFore() 
     * @return
     */
    public abstract int getFore();
    
    /**
     * Zwraca przypisany numer koloru tła. Numer może być zamieniony na
     * graficzną reprezentację za pomocą {@link AnsiColorScheme}. 
     * @see #getComputedBack() 
     * @return
     */
    public abstract int getBack();
    
    /**
     * Zwraca przypisane atrybuty tekstu.
     * @return
     */
    public abstract int getFlags();
    
    /**
     * Zwraca przypisany numer czcionki. Numer może być zamieniony na
     * graficzną reprezentację za pomocą {@link AnsiFontScheme}.
     * @see #getComputedFont()
     * @return
     */
    public abstract int getFont();
    
    /**
     * Metoda opcjonalna: zwraca przypisany kolor tekstu. Ogólnie metoda zwraca rozsądny
     * wynik, jeśli obiekt został powiązany z odpowiednim schematem kolorów
     * ({@link AnsiColorScheme}.
     * @return
     */
    public abstract Color getComputedFore();
    
    /**
     * Metoda opcjonalna: zwraca przypisany kolor tła. Ogólnie metoda zwraca rozsądny
     * wynik, jeśli obiekt został powiązany z odpowiednim schematem kolorów
     * ({@link AnsiColorScheme}.
     * @return
     */
    public abstract Color getComputedBack();

    /**
     * Metoda opcjonalna: zwraca przypisaną czcionkę. Ogólnie metoda zwraca rozsądny
     * wynik, jeśli obiekt został powiązany z odpowiednim schematem czcionek
     * ({@link AnsiFontScheme}.
     * @return
     */
    public abstract Font getComputedFont();
    
    public final boolean isDefault() {
        return 
            0 == getBack() &&
            0 == getFore() &&
            0== getFont() &&
            AnsiAttr.NORMAL == getFlags();
    }
    
    public final boolean isComputedDefault() {
        return 
            null == getComputedBack() &&
            null == getComputedFore() &&
            null == getComputedFont() &&
            AnsiAttr.NORMAL == getFlags();
    }
    
    public static class Value extends AnsiAttr {
        
        private static final long serialVersionUID = 1L;
        
        protected final int back;
        protected final int fore;
        protected final int font;
        protected final int flags;
        
        public Value(int back, int fore, int font, int flags) {
            this.back = back;
            this.fore = fore;
            this.font = font;
            this.flags = flags;
        }

        @Override
        public final int getFore() {
            return fore;
        }

        @Override
        public final int getBack() {
            return back;
        }

        @Override
        public final int getFlags() {
            return flags;
        }

        @Override
        public final int getFont() {
            return font;
        }

        @Override
        public Color getComputedFore() {
            return null;
        }

        @Override
        public Color getComputedBack() {
            return null;
        }

        @Override
        public Font getComputedFont() {
            return null;
        }
        
    }
    
    public static class Mutable extends AnsiAttr {
        
        private static final long serialVersionUID = 1L;
        
        protected int back;
        protected int fore;
        protected int font;
        protected int flags;
        
        public Mutable() {
            this(0, 0, 0, AnsiAttr.NORMAL);
        }
        
        public Mutable(int back, int fore, int font, int flags) {
            this.back = back;
            this.fore = fore;
            this.font = font;
            this.flags = flags;
        }

        @Override
        public final int getFore() {
            return fore;
        }

        @Override
        public final int getBack() {
            return back;
        }

        @Override
        public final int getFlags() {
            return flags;
        }

        @Override
        public final int getFont() {
            return font;
        }

        @Override
        public Color getComputedFore() {
            return null;
        }

        @Override
        public Color getComputedBack() {
            return null;
        }

        @Override
        public Font getComputedFont() {
            return null;
        }
        
    }
    

}
