1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package optkl.textmodel.terminal;
 26 
 27 import optkl.textmodel.tokens.Token;
 28 
 29 import java.io.PrintStream;
 30 import java.util.Arrays;
 31 import java.util.function.Consumer;
 32 import java.util.function.Function;
 33 
 34 public interface ANSI<T extends ANSI<T>> extends  Function<String, T> {
 35     T apply(String o);
 36 
 37     default T apply(Token t) {
 38         return apply(t.asString());
 39     }
 40 
 41     default T self() {
 42         return (T) this;
 43     }
 44 
 45     default void skip(){
 46     }
 47 
 48     int BLACK = 30;
 49     int RED = 31;
 50     int GREEN = 32;
 51     int YELLOW = 33;
 52     int BLUE = 34;
 53     int PURPLE = 35;
 54     int CYAN = 36;
 55     int WHITE = 37;
 56 
 57     default T esc() {
 58         return apply("\033");
 59     }
 60 
 61     default T color(int c, Consumer<T> cc) {
 62         csi().ints(c).apply("m");
 63         cc.accept(self());
 64         return reset();
 65     }
 66 
 67     default T color(int c1, int c2, Consumer<T> cc) {
 68         csi().ints(c1, c2).apply("m");
 69         cc.accept(self());
 70 
 71         return reset();
 72     }
 73     default T fg(int color, Consumer<T> cc) {
 74         return color(0, color, cc);
 75     }
 76 
 77 
 78     default T bold(int color, Consumer<T> cc) {
 79         return color(1, color, cc);
 80     }
 81 
 82 
 83     default T boldAndBright(int color, Consumer<T> cc) {
 84         return color(0, color + 60, cc);
 85     }
 86 
 87 
 88 
 89     default T bright(int color, Consumer<T> cc) {
 90         return color(1, color + 60, cc);
 91     }
 92 
 93 
 94 
 95     default T bg(int color,Consumer<T> cc) {
 96         return color(color + 10, cc);
 97     }
 98 
 99     default T csi() {
100         return esc().apply("[");
101     }
102 
103     default T home(){
104         return csi().ints(0,0).apply("H");
105     }
106 
107     default T csiQuery() {
108         return csi().apply("?");
109     }
110 
111     default T ints(int... n) {
112         apply(String.valueOf(n[0]));
113         for (int i = 1; i < n.length; i++) {
114             apply(";" + n[i]);
115         }
116         return self();
117     }
118 
119 
120     default T inv() {
121         return csi().ints(7).apply("m");
122     }
123 
124     default T reset() {
125         return csi().ints(0).apply("m");
126     }
127 
128     default T inv(Consumer<T> c) {
129         inv();
130         c.accept(self());
131         return reset();
132     }
133 
134 
135 
136     record Adaptor(Consumer<String> consumer) implements ANSI<Adaptor> {
137         @Override
138         public Adaptor apply(String s) {
139             consumer.accept(s);
140             return self();
141         }
142     }
143 
144     default T repeat(String s, int count) {
145         return apply(s.repeat(count));
146     }
147 
148     default T fill(int cols, String s) {
149         return apply(s).repeat(" ", Math.max(0, cols - s.length()));
150     }
151 
152     class IMPL implements ANSI<IMPL> {
153         private final PrintStream printStream;
154 
155         @Override
156         public IMPL apply(String s) {
157             printStream.append(s);
158             return self();
159         }
160 
161         IMPL(PrintStream printStream) {
162             this.printStream = printStream;
163         }
164     }
165 
166     static IMPL of(PrintStream printStream) {
167         return new IMPL(printStream);
168     }
169 
170 
171      class DotImage {
172         final private ANSI<?> ansi;
173         final public int width;
174         final public int height;
175         final private byte[] bytes;
176         int charWidth;
177         int charHeight;
178         final private  char[] chars;
179         public DotImage(ANSI<?> ansi, int width, int height){
180             this.ansi = ansi;
181             this.width = width;
182             this.height = height;
183             this.bytes = new byte[width*height];
184             this.charWidth = width/2;
185             this.charHeight = height/4;
186             this.chars = new char[charWidth*charHeight];
187         }
188         public void set(int x, int y){
189             bytes[(y*width)+x]=1;//0xff;
190         }
191         void reset(int x, int y){
192             bytes[(y*width)+x]=0;
193         }
194         int i(int x, int y){
195             byte b = bytes[(y*width)+x];
196             return (int)(b<0?b+256:b);
197         }
198         /**
199          See the unicode mapping table here
200          https://images.app.goo.gl/ntxis4mKzn7GmrGb7
201          */
202         char dotchar(int bytebits){
203            // int mapped = (bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80);
204             //char brail = (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
205             return  (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
206         }
207         public DotImage home(){
208 
209             ansi.home();
210             return this;
211         }
212         public DotImage delay(int ms){
213             try{ Thread.sleep(ms); }catch(Throwable t){ }
214             return this;
215         }
216 
217         public DotImage clean(){
218             Arrays.fill(bytes,(byte)0);
219             Arrays.fill(chars,(char)' ');
220             return this;
221         }
222         public DotImage map(){
223             for (int cx = 0; cx<charWidth; cx++){
224                 for (int cy = 0; cy<charHeight; cy++){
225                     int bytebits=0;
226                     for (int dx=0;dx<2;dx++){
227                         for (int dy=0;dy<4;dy++){
228                             bytebits|=i(cx*2+dx,cy*4+dy)<<(dx*4+dy);
229                         }
230                     }
231                     chars[cy*charWidth+cx]= dotchar(bytebits);
232                 }
233             }
234             return this;
235         }
236 
237         public DotImage write(){
238             ansi.apply("+");
239             for (int i=0;i<charWidth; i++){
240                 ansi.apply("-");
241             }
242             ansi.apply("+\n|");
243             for (int i=0;i<chars.length; i++){
244                 if (i>0 && (i%charWidth)==0){
245                     ansi.apply("|\n|");
246                 }
247                 ansi.apply(Character.toString(chars[i]));
248             }
249             ansi.apply("|\n+");
250             for (int i=0;i<charWidth; i++){
251                 ansi.apply("-");
252             }
253             ansi.apply("+\n");
254             return this;
255         }
256     }
257     default DotImage img(int width, int height){
258         return new DotImage(this, width,height);
259     }
260 
261 }