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     default T apply(char ch){
 37         return apply(""+ch);
 38     }
 39     default T apply(Token t) {
 40         return apply(t.asString());
 41     }
 42 
 43     default T self() {
 44         return (T) this;
 45     }
 46 
 47     default void skip(){
 48     }
 49 
 50     int BLACK = 30;
 51     int RED = 31;
 52     int GREEN = 32;
 53     int YELLOW = 33;
 54     int BLUE = 34;
 55     int PURPLE = 35;
 56     int CYAN = 36;
 57     int WHITE = 37;
 58 
 59     default T esc() {
 60         return apply("\033");
 61     }
 62 
 63     default T color(int c, Consumer<T> cc) {
 64         csi().ints(c).apply("m");
 65         cc.accept(self());
 66         return reset();
 67     }
 68 
 69     default T color(int c1, int c2, Consumer<T> cc) {
 70         csi().ints(c1, c2).apply("m");
 71         cc.accept(self());
 72 
 73         return reset();
 74     }
 75     default T fg(int color, Consumer<T> cc) {
 76         return color(0, color, cc);
 77     }
 78     default T fg(int color, Token token) {
 79         return fg(color, $->$.apply(token));
 80     }
 81 
 82 
 83     default T bold(int color, Consumer<T> cc) {
 84         return color(1, color, cc);
 85     }
 86 
 87 
 88     default T boldAndBright(int color, Consumer<T> cc) {
 89         return color(0, color + 60, cc);
 90     }
 91 
 92 
 93 
 94     default T bright(int color, Consumer<T> cc) {
 95         return color(1, color + 60, cc);
 96     }
 97 
 98 
 99 
100     default T bg(int color,Consumer<T> cc) {
101         return color(color + 10, cc);
102     }
103     default T bg(int color,Token token) {
104         return bg(color, $->$.apply(token));
105     }
106 
107     default T csi() {
108         return esc().apply("[");
109     }
110 
111     default T home(){
112         return csi().ints(0,0).apply("H");
113     }
114 
115     default T csiQuery() {
116         return csi().apply("?");
117     }
118 
119     default T ints(int... n) {
120         apply(String.valueOf(n[0]));
121         for (int i = 1; i < n.length; i++) {
122             apply(";" + n[i]);
123         }
124         return self();
125     }
126 
127 
128     default T inv() {
129         return csi().ints(7).apply("m");
130     }
131 
132     default T reset() {
133         return csi().ints(0).apply("m");
134     }
135 
136     default T inv(Consumer<T> c) {
137         inv();
138         c.accept(self());
139         return reset();
140     }
141 
142 
143 
144     record Adaptor(Consumer<String> consumer) implements ANSI<Adaptor> {
145         @Override
146         public Adaptor apply(String s) {
147             consumer.accept(s);
148             return self();
149         }
150     }
151 
152     default T repeat(String s, int count) {
153         return apply(s.repeat(count));
154     }
155 
156     default T fill(int cols, String s) {
157         return apply(s).repeat(" ", Math.max(0, cols - s.length()));
158     }
159 
160     class IMPL implements ANSI<IMPL> {
161         private final PrintStream printStream;
162 
163         @Override
164         public IMPL apply(String s) {
165             printStream.append(s);
166             return self();
167         }
168 
169         IMPL(PrintStream printStream) {
170             this.printStream = printStream;
171         }
172     }
173 
174     static IMPL of(PrintStream printStream) {
175         return new IMPL(printStream);
176     }
177 
178 
179      class DotImage {
180         final private ANSI<?> ansi;
181         final public int width;
182         final public int height;
183         final private byte[] bytes;
184         int charWidth;
185         int charHeight;
186         final private  char[] chars;
187         public DotImage(ANSI<?> ansi, int width, int height){
188             this.ansi = ansi;
189             this.width = width;
190             this.height = height;
191             this.bytes = new byte[width*height];
192             this.charWidth = width/2;
193             this.charHeight = height/4;
194             this.chars = new char[charWidth*charHeight];
195         }
196         public void set(int x, int y){
197             bytes[(y*width)+x]=1;//0xff;
198         }
199         void reset(int x, int y){
200             bytes[(y*width)+x]=0;
201         }
202         int i(int x, int y){
203             byte b = bytes[(y*width)+x];
204             return (int)(b<0?b+256:b);
205         }
206         /**
207          See the unicode mapping table here
208          https://images.app.goo.gl/ntxis4mKzn7GmrGb7
209          */
210         char dotchar(int bytebits){
211            // int mapped = (bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80);
212             //char brail = (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
213             return  (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
214         }
215         public DotImage home(){
216 
217             ansi.home();
218             return this;
219         }
220         public DotImage delay(int ms){
221             try{ Thread.sleep(ms); }catch(Throwable t){ }
222             return this;
223         }
224 
225         public DotImage clean(){
226             Arrays.fill(bytes,(byte)0);
227             Arrays.fill(chars,(char)' ');
228             return this;
229         }
230         public DotImage map(){
231             for (int cx = 0; cx<charWidth; cx++){
232                 for (int cy = 0; cy<charHeight; cy++){
233                     int bytebits=0;
234                     for (int dx=0;dx<2;dx++){
235                         for (int dy=0;dy<4;dy++){
236                             bytebits|=i(cx*2+dx,cy*4+dy)<<(dx*4+dy);
237                         }
238                     }
239                     chars[cy*charWidth+cx]= dotchar(bytebits);
240                 }
241             }
242             return this;
243         }
244 
245         public DotImage write(){
246             ansi.apply("+");
247             for (int i=0;i<charWidth; i++){
248                 ansi.apply("-");
249             }
250             ansi.apply("+\n|");
251             for (int i=0;i<chars.length; i++){
252                 if (i>0 && (i%charWidth)==0){
253                     ansi.apply("|\n|");
254                 }
255                 ansi.apply(Character.toString(chars[i]));
256             }
257             ansi.apply("|\n+");
258             for (int i=0;i<charWidth; i++){
259                 ansi.apply("-");
260             }
261             ansi.apply("+\n");
262             return this;
263         }
264     }
265     default DotImage img(int width, int height){
266         return new DotImage(this, width,height);
267     }
268 
269 }