package org.yamcs.xtceproc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ErrorInCommand;
import org.yamcs.parameter.Value;
import org.yamcs.xtce.*;
public class MetaCommandContainerProcessor {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
TcProcessingContext pcontext;
ArgumentTypeProcessor argumentTypeProcessor;
MetaCommandContainerProcessor(TcProcessingContext pcontext) {
this.pcontext = pcontext;
argumentTypeProcessor = new ArgumentTypeProcessor(pcontext.pdata);
}
public void encode(MetaCommand metaCommand) throws ErrorInCommand {
MetaCommand parent = metaCommand.getBaseMetaCommand();
if(parent !=null ) {
encode(parent);
}
MetaCommandContainer container = metaCommand.getCommandContainer();
if(container == null) {
throw new ErrorInCommand("MetaCommand has no container: " + metaCommand);
}
for(SequenceEntry se: container.getEntryList()) {
int size=0;
int previousPosition = pcontext.bitPosition;
switch(se.getReferenceLocation()) {
case previousEntry:
pcontext.bitPosition += se.getLocationInContainerInBits();
break;
case containerStart:
pcontext.bitPosition = se.getLocationInContainerInBits();
}
if(se instanceof ArgumentEntry) {
fillInArgumentEntry((ArgumentEntry) se, pcontext);
size = (pcontext.bitPosition+7)/8;
} else if (se instanceof FixedValueEntry) {
fillInFixedValueEntry((FixedValueEntry) se, pcontext);
size = (pcontext.bitPosition+7)/8;
}
if(size>pcontext.size) {
pcontext.size = size;
}
}
}
private void fillInArgumentEntry(ArgumentEntry argEntry, TcProcessingContext pcontext) {
Argument arg = argEntry.getArgument();
Value argValue = pcontext.getArgumentValue(arg);
if(argValue==null) {
throw new IllegalStateException("No value for argument "+arg);
}
ArgumentType atype = arg.getArgumentType();
Value rawValue = argumentTypeProcessor.decalibrate(atype, argValue);
pcontext.deEncoder.encodeRaw(((BaseDataType) atype).getEncoding(), rawValue);
}
private void fillInFixedValueEntry(FixedValueEntry fve, TcProcessingContext pcontext) {
int sizeInBits = fve.getSizeInBits();
//CAREFUL: do not change v1 since it is supposed to be final
final byte[] v1 = fve.getBinaryValue();
//shift v1 into v2 to be byte aligned with the pcontext.bitPostion
int fb1 = sizeInBits&0x07; //number of bits in the leftmost byte in v
int fb2 = 8 - pcontext.bitPosition&0x07; //number if bits in the leftmost byte in the pcontext
byte[]v2;
int bitshift;
if(fb1>fb2) {//shift to right
int shift = fb1 - fb2;
v2 = new byte[v1.length+1];
int bits=0;
int mask = -1>>>(32-shift);
for(int i = 0; i < v1.length; i++) {
v2[i] = (byte) (bits | ((v1[i]&0xFF) >> shift));
bits = (v1[i] & mask) <<(8-shift);
}
v2[v1.length] = (byte)bits;
bitshift = 8-shift;
} else if(fb1<fb2){//shift to left
int shift = fb2 - fb1;
v2 = new byte[v1.length+1];
int mask = -1>>>(24+shift);
int bits=0;
for(int i = 0; i < v1.length; i++) {
v2[i] = (byte) (bits | (v1[i]&0xFF)>>(8-shift));
bits = (v1[i] & mask)<<shift;
}
v2[v1.length] = (byte)bits;
bitshift = shift;
} else {
v2 = v1;
bitshift = 0;
}
//number of bytes to copy from v2 into pcontext.bb;
// the first and last are potentially only partially copied
//NOTE that v1 and implicitly v2 could potentially have more bytes than required by sizeInBits
int bytesToCopy = (bitshift+sizeInBits+7)/8;
//the first byte in v2 which has to be copied
int startByte = v2.length-bytesToCopy;
if(bytesToCopy==1) { //special case first and last byte are the same
byte x = pcontext.bb.get(pcontext.bitPosition/8);
int mask = (~(-1<<sizeInBits))<<bitshift;
pcontext.bb.put(pcontext.bitPosition/8, (byte)(x&~mask | (v2[startByte]&mask)));
pcontext.bitPosition+=sizeInBits;
} else {
int bitsToMergeFromFirstByte = (bitshift + sizeInBits) & 0x7;
if(bitsToMergeFromFirstByte!=0 ) { //first byte
byte x = pcontext.bb.get(pcontext.bitPosition/8);
int mask = ~(-1<<bitsToMergeFromFirstByte);
pcontext.bb.put(pcontext.bitPosition/8, (byte)(x&~mask | (v2[startByte]&mask)));
pcontext.bitPosition+=bitsToMergeFromFirstByte;
bytesToCopy--;
startByte++;
}
if(bitshift>0) {
bytesToCopy--;
}
for(int i=0; i<bytesToCopy; i++) { //the middle part //could be optimised using a bulk put method
pcontext.bb.put(pcontext.bitPosition/8, v2[startByte+i]);
pcontext.bitPosition+=8;
}
if(bitshift>0) { //last byte
byte x = pcontext.bb.get(pcontext.bitPosition/8);
x = (byte) (x & ~(-1<<bitshift));
pcontext.bb.put(pcontext.bitPosition/8, (byte)(x | v2[v2.length-1]));
pcontext.bitPosition+=bitshift;
}
}
}
}