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