package puzzle;

import module java.base;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.val;
import org.junit.jupiter.api.Test;
import precomp.Neighbors9x8;
import puzzle.SwedishGenerator.Dict;
import puzzle.SwedishGenerator.DictEntry;
import puzzle.SwedishGenerator.Lemma;
import puzzle.dict950.DictData950;
import static java.nio.charset.StandardCharsets.US_ASCII;

public final class DictJavaGeneratorMulti {
   
   @Test
   void testReversed() {
      val wOrig = DictData950.DICT950.index()[4].words()[4];
      val wRev  = DictData950.DICT950.reversed()[4].words()[4];
      System.out.println(Lemma.asWord(wOrig, new byte[8]) + " " + Lemma.asWord(wRev, new byte[8]));
   }
   interface Dicts {
      
      static Dict makeDict(long[] wordz) {
         var index = new DictEntryDTO[Neighbors9x8.MAX_WORD_LENGTH_PLUS_ONE];
         Arrays.setAll(index, DictEntryDTO::new);
         for (var lemma : wordz) {
            var L     = Lemma.unpackSize(lemma) + 1;//Lemma.unpackSize(lemma) + 2;
            val entry = index[L];
            val idx   = entry.words().size();
            val pos   = entry.pos();
            entry.words().add(lemma);
            var i = 0;
            for (var w = lemma & Lemma.LETTER_MASK; w != 0; w >>>= 5, i++) {
               pos[i][(int) ((w & 31) - 1)].add(idx);
            }
         }
         for (var i = 2; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i);
         return new Dict(Arrays.stream(index).map(i -> {
            var words    = i.words().toArray();
            var numWords = words.length;
            var numLongs = (numWords + 63) >>> 6;
            var bitsets  = new long[i.pos().length * 26][numLongs];
            for (var p = 0; p < i.pos().length; p++) {
               for (var l = 0; l < 26; l++) {
                  var list = i.pos()[p][l];
                  var bs   = bitsets[p * 26 + l];
                  for (var k = 0; k < list.size(); k++) {
                     var wordIdx = list.data()[k];
                     bs[wordIdx >>> 6] |= (1L << (wordIdx & 63));
                  }
               }
            }
            return new DictEntry(words, bitsets, words.length, (words.length + 63) >>> 6);
         }).toArray(DictEntry[]::new),
                         Arrays.stream(index).mapToInt(i -> i.words().size()).sum());
      }
   }
   
   record DictEntryDTO(LongArrayList words, IntListDTO[][] pos) {
      
      public DictEntryDTO(int L) {
         this(new LongArrayList(1024), new IntListDTO[L][26]);
         for (var i = 0; i < L; i++) for (var j = 0; j < 26; j++) pos[i][j] = new IntListDTO();
      }
      @Getter
      @Accessors(fluent = true)
      @NoArgsConstructor
      static final class IntListDTO {
         
         int[] data = new int[8];
         int   size = 0;
         void add(int v) {
            if (size >= data.length) data = Arrays.copyOf(data, data.length * 2);
            data[size++] = v;
         }
      }
   }
   
   static final class LongArrayList {
      
      long[] a;
      int    size;
      
      LongArrayList(int initialCapacity) {
         if (initialCapacity < 0) throw new IllegalArgumentException();
         a = new long[initialCapacity];
      }
      
      int size() { return size; }
      
      void add(long v) {
         if (size == a.length) grow();
         a[size++] = v;
      }
      
      void grow() {
         var newCap = a.length == 0 ? 1 : a.length * 2;
         var n      = new long[newCap];
         System.arraycopy(a, 0, n, 0, size);
         a = n;
      }
      long[] toArray() { return Arrays.copyOf(a, this.size); }
   }
}
