package net.ranides.assira.text;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.spi.CharsetProvider;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import net.ranides.assira.collection.sets.HashSet;

public class MazoviaCharset extends CharsetProvider {

    private static final String NAME = "mazovia";
    
    private static final String[] NAMES = new String[]{
        "cp-896",
        "cp896",
        "cp620",
        "cp-620",
        "cp790",
        "cp-790"
    };
    
    static final MCharset CHARSET = new MCharset();
    
    private static final Set<String> NAMES_SET = new HashSet<>(NAMES);
    
    private static final List<Charset> CHARSETS = Arrays.asList(CHARSET);
    
    private static final char[][] CHARMAP = new char[][]{
        {'\u0104', 143}//Ą
      , {'\u0105', 134}//ą
      , {'\u0106', 149}//Ć
      , {'\u0107', 141}//ć
      , {'\u0118', 144}//Ę
      , {'\u0119', 145}//ę
      , {'\u0141', 156}//Ł
      , {'\u0142', 146}//ł
      , {'\u0143', 165}//Ń
      , {'\u0144', 164}//ń
      , {'\u00D3', 163}//Ó
      , {'\u00F3', 162}//ó
      , {'\u015A', 152}//Ś
      , {'\u015B', 158}//ś
      , {'\u0179', 160}//Ź
      , {'\u017A', 166}//ź
      , {'\u017B', 161}//Ż
      , {'\u017C', 167}//ż
    };

    @Override
    public Iterator<Charset> charsets() {
        return CHARSETS.iterator();
    }

    @Override
    public Charset charsetForName(String charsetName) {
        if(NAME.equalsIgnoreCase(charsetName)){
            return CHARSETS.get(0);
        }
        if (NAMES_SET.contains(charsetName.toLowerCase(Locale.ROOT).trim())) {
            return CHARSETS.get(0);
        } 
        return null;
    }
    
    private static class MCharset extends Charset {

        public MCharset() {
            super(NAME, NAMES);
        }

        @Override
        public boolean contains(Charset cs) {
            return cs.equals(this);
        }
        
        @Override
        public CharsetEncoder newEncoder() {
            return new MEncoder(this);
        }

        @Override
        public CharsetDecoder newDecoder() {
            return new MDecoder(this);
        }

    }
    
    private static class MEncoder extends CharsetEncoder {

        public MEncoder(Charset cs) {
            super(cs, 1, 1);
        }

        @Override
        protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
            while (in.hasRemaining()) {
                out.put((byte) (encodeChar(in.get()) & 0xFF));
            }
            return CoderResult.UNDERFLOW;
        }

        private char encodeChar(char c) {
            for (char[] m : CHARMAP) {
                if (c == m[0]) {
                    return m[1];
                }
            }
            return c;
        }
    }

    private static class MDecoder extends CharsetDecoder {

        public MDecoder(Charset cs) {
            super(cs, 1, 1);
        }

        @Override
        protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
            while (in.hasRemaining()) {
                if(out.remaining() < 1) {
                    return CoderResult.OVERFLOW;
                }
                out.put(decodeChar((char) (in.get() & 0x00FF)));
            }
            return CoderResult.UNDERFLOW;
        }

        private char decodeChar(char c) {
            for (char[] m : CHARMAP) {
                if (c == m[1]) {
                    return m[0];
                }
            }
            return c;
        }
    }

}
