427a179230c49cb36dd975e79dd76c1469bbb513
[Mograsim.git] / plugins / net.mograsim.logic.model / src / net / mograsim / logic / model / model / components / atomic / SimpleRectangularHardcodedModelComponent.java
1 package net.mograsim.logic.model.model.components.atomic;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.Objects;
6 import java.util.concurrent.atomic.AtomicReference;
7 import java.util.function.Consumer;
8
9 import net.haspamelodica.swt.helper.gcs.GeneralGC;
10 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
11 import net.mograsim.logic.core.wires.CoreWire.ReadEnd;
12 import net.mograsim.logic.core.wires.CoreWire.ReadWriteEnd;
13 import net.mograsim.logic.model.model.LogicModelModifiable;
14 import net.mograsim.logic.model.model.components.ModelComponent;
15 import net.mograsim.logic.model.model.wires.Pin;
16 import net.mograsim.logic.model.modeladapter.LogicCoreAdapter;
17 import net.mograsim.logic.model.modeladapter.componentadapters.SimpleRectangularHardcodedModelComponentAdapter;
18 import net.mograsim.logic.model.serializing.IdentifyParams;
19 import net.mograsim.logic.model.snippets.HighLevelStateHandler;
20 import net.mograsim.logic.model.snippets.outlinerenderers.DefaultOutlineRenderer;
21 import net.mograsim.logic.model.snippets.symbolrenderers.CenteredTextSymbolRenderer;
22 import net.mograsim.logic.model.snippets.symbolrenderers.CenteredTextSymbolRenderer.CenteredTextParams;
23 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer;
24 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams;
25 import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams.Position;
26 import net.mograsim.logic.model.util.ObservableAtomicReference;
27
28 public abstract class SimpleRectangularHardcodedModelComponent extends ModelComponent
29 {
30         private static final double centerTextHeight = 5;
31         private static final double pinNamesHeight = 3.5;
32         private static final double pinNamesMargin = .5;
33
34         private final String id;
35
36         private final DefaultOutlineRenderer outlineRenderer;
37         private final CenteredTextSymbolRenderer centerTextRenderer;
38         private final PinNamesSymbolRenderer pinNamesRenderer;
39
40         private ObservableAtomicReference<Object> state;
41         private Runnable recalculate;
42
43         private final Map<String, Map<Consumer<Object>, Consumer<ObservableAtomicReference<Object>>>> stateObsPerHLSListenerPerStateID;
44
45         // creation and destruction
46
47         public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText)
48         {
49                 this(model, id, name, centerText, true);
50         }
51
52         public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText, boolean callInit)
53         {
54                 super(model, name, false);
55                 this.id = id;
56                 this.outlineRenderer = new DefaultOutlineRenderer(this);
57                 CenteredTextParams centeredTextParams = new CenteredTextParams();
58                 centeredTextParams.text = centerText;
59                 centeredTextParams.fontHeight = centerTextHeight;
60                 this.centerTextRenderer = new CenteredTextSymbolRenderer(this, centeredTextParams);
61                 PinNamesParams pinNamesParams = new PinNamesParams();
62                 pinNamesParams.pinLabelHeight = pinNamesHeight;
63                 pinNamesParams.pinLabelMargin = pinNamesMargin;
64                 this.pinNamesRenderer = new PinNamesSymbolRenderer(this, pinNamesParams);
65                 addPinRemovedListener(this::pinRemoved);
66
67                 this.stateObsPerHLSListenerPerStateID = new HashMap<>();
68
69                 setHighLevelStateHandler(new HighLevelStateHandler()
70                 {
71
72                         @Override
73                         public Object get(String stateID)
74                         {
75                                 return getHighLevelState(state.get(), stateID);
76                         }
77
78                         @Override
79                         public void set(String stateID, Object newState)
80                         {
81                                 state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState));
82                                 recalculate.run();
83                         }
84
85                         @Override
86                         public void addListener(String stateID, Consumer<Object> stateChanged)
87                         {
88                                 addHighLevelStateListener(state.get(), stateID, stateChanged);
89                         }
90
91                         @Override
92                         public void removeListener(String stateID, Consumer<Object> stateChanged)
93                         {
94                                 removeHighLevelStateListener(state.get(), stateID, stateChanged);
95                         }
96
97                         @Override
98                         public String getIDForSerializing(IdentifyParams idParams)
99                         {
100                                 return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent
101                         }
102
103                         @Override
104                         public Object getParamsForSerializing(IdentifyParams idParams)
105                         {
106                                 return null;
107                         }
108                 });
109
110                 if (callInit)
111                         init();
112         }
113
114         // pins
115
116         protected void addPin(Pin pin, Position namePosition)
117         {
118                 super.addPin(pin); // do this first to catch errors
119                 pinNamesRenderer.setPinPosition(pin, namePosition);
120         }
121
122         private void pinRemoved(Pin pin)
123         {
124                 pinNamesRenderer.setPinPosition(pin, null);
125         }
126
127         // high-level access
128
129         @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden
130         protected Object getHighLevelState(Object state, String stateID)
131         {
132                 throw new IllegalArgumentException("No high level state with ID " + stateID);
133         }
134
135         @SuppressWarnings({ "static-method", "unused" }) // this method is intended to be overridden
136         protected Object setHighLevelState(Object lastState, String stateID, Object newHighLevelState)
137         {
138                 throw new IllegalArgumentException("No high level state with ID " + stateID);
139         }
140
141         protected void addHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
142         {
143                 AtomicReference<Object> lastHLSRef = new AtomicReference<>(getHighLevelState(state, stateID));
144                 Consumer<ObservableAtomicReference<Object>> refObs = r ->
145                 {
146                         Object newHLS = getHighLevelState(stateID);
147                         if (!Objects.equals(lastHLSRef.getAndSet(newHLS), newHLS))
148                                 stateChanged.accept(newHLS);
149                 };
150                 stateObsPerHLSListenerPerStateID.computeIfAbsent(stateID, s -> new HashMap<>()).put(stateChanged, refObs);
151                 this.state.addObserver(refObs);
152         }
153
154         protected void removeHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
155         {
156                 getHighLevelState(state, stateID);// if this throws, we know there is no HLS with this name
157                 var stateObsPerHLSListener = stateObsPerHLSListenerPerStateID.get(stateID);
158                 if (stateObsPerHLSListener == null)
159                         return;
160                 Consumer<ObservableAtomicReference<Object>> refObs = stateObsPerHLSListener.remove(stateChanged);
161                 this.state.removeObserver(refObs);
162         }
163
164         // logic
165
166         public abstract Object recalculate(Object lastState, Map<String, ReadEnd> readEnds, Map<String, ReadWriteEnd> readWriteEnds);
167
168         // core model binding
169
170         public void setCoreModelBindingAndResetState(ObservableAtomicReference<Object> state, Runnable recalculate)
171         {
172                 this.state = state;
173                 this.recalculate = recalculate;
174         }
175
176         // "graphical" operations
177
178         @Override
179         public void render(GeneralGC gc, Rectangle visibleRegion)
180         {
181                 outlineRenderer.render(gc, visibleRegion);
182                 centerTextRenderer.render(gc, visibleRegion);
183                 pinNamesRenderer.render(gc, visibleRegion);
184         }
185
186         // serializing
187
188         @Override
189         public String getIDForSerializing(IdentifyParams idParams)
190         {
191                 return id;
192         }
193
194         // operations no longer supported
195
196         @Override
197         protected void addPin(Pin pin)
198         {
199                 throw new UnsupportedOperationException("Can't add pins without setting usage, call addPin(Pin [, Position]) instead");
200         }
201
202         static
203         {
204                 LogicCoreAdapter.addComponentAdapter(new SimpleRectangularHardcodedModelComponentAdapter());
205         }
206 }