/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package mdemangler.naming;

import mdemangler.*;

/**
 * This class represents one component of a namespace qualification (see
 * MDQualification).
 */
public class MDQualifier extends MDParsableItem {
	private static final String ANONYMOUS_NAMESPACE = "`anonymous namespace'";
	private static final String UNKNOWN_NAMESPACE = "MDMANG_UNK_QUALIFICATION";
	private MDReusableName name;
	private MDReusableName templateName;
	private MDReusableName nameAnonymous;
	private MDReusableName nameInterface;
	private MDNestedName nameNested;
	private MDNumberedNamespace nameNumbered;
	private MDQualification nameQ;
	private MDFragmentName nameC; // Windows 10 stuff

	public MDQualifier(MDMang dmang) {
		super(dmang);
	}

	//====

	public boolean isName() {
		return name != null;
	}

	public boolean isTemplate() {
		return templateName != null;
	}

	public boolean isAnon() {
		return nameAnonymous != null;
	}

	public boolean isInterface() {
		return nameInterface != null;
	}

	public boolean isNested() {
		return nameNested != null;
	}

	// possibly delete this one and use isNameNumbered() instead; TODO
	public boolean isLocalNamespace() {
		return nameNumbered != null;
	}

	public boolean isNameNumbered() {
		return nameNumbered != null;
	}

	public boolean isNameQ() {
		return nameQ != null;
	}

	public boolean isNameC() {
		return nameC != null;
	}

	//====

	public MDReusableName getName() {
		return name;
	}

	public MDReusableName getTemplate() {
		return templateName;
	}

	public String getAnonymousName() {
		return nameAnonymous.getName();
	}

	public MDReusableName getInterface() {
		return nameInterface;
	}

	public MDNestedName getNested() {
		return nameNested;
	}

	// possibly delete this one and use getNameNumbered() instead; TODO
	public String getLocalNamespace() {
		return nameNumbered.getName();
	}

	// possibly delete this one and use getNameNumbered() instead; TODO
	public String getLocalNamespaceNumber() {
		return nameNumbered.getNumber().toString();
	}

	public MDNumberedNamespace getNameNumbered() {
		return nameNumbered;
	}

	public MDQualification getNameQ() {
		return nameQ;
	}

	public MDFragmentName getNameC() {
		return nameC;
	}

	@Override
	public void insert(StringBuilder builder) {
		// Only one of these will hit.
		if (name != null) {
			name.insert(builder);
		}
		else if (templateName != null) {
			templateName.insert(builder);
		}
		else if (nameAnonymous != null) {
			if (dmang.getOutputOptions().useEncodedAnonymousNamespace()) {
				dmang.insertString(builder,
					MDMangUtils.createStandardAnonymousNamespaceNode(nameAnonymous.getName()));
			}
			else {
				dmang.insertString(builder, ANONYMOUS_NAMESPACE);
			}
		}
		else if (nameInterface != null) {
			nameInterface.insert(builder);
		}
		else if (nameNested != null) {
			nameNested.insert(builder);
		}
		else if (nameNumbered != null) {
			nameNumbered.insert(builder);
		}
		else if (nameQ != null) {
			// Could create a new object for this type and modify its insert method (and getName
			//  method) to return the right things.  As it is, the above access getNameQ method
			//  will return an MDQualification that has no concept of the brackets here.  But
			//  it could be remedied with the separate object.  Similarly (but no bracket issue),
			//  the "nameC" could also get its own type to represent its object, but we don't
			//  quite know what that is at this time.
			StringBuilder nameQBuilder = new StringBuilder();
			nameQ.insert(nameQBuilder);
			dmang.insertString(nameQBuilder, "[");
			dmang.appendString(nameQBuilder, "]");
			String str = nameQBuilder.toString();
			dmang.insertString(builder, str);
		}
		else if (nameC != null) {
			nameC.insert(builder);
		}
		else {
			dmang.insertString(builder, UNKNOWN_NAMESPACE);
		}
	}

	@Override
	protected void parseInternal() throws MDException {
		if (dmang.peek() == '?') {
			switch (dmang.peek(1)) {
				case '?':
					nameNested = new MDNestedName(dmang);
					nameNested.parse();
					break;
				case '$':
					// This is a template, but it will get processed through MDReusableName.
					templateName = new MDReusableName(dmang);
					templateName.parse();
					break;
				case 'A': // Anonymous namespace
					// 20140522 found that we should Keep the 'A' as part of the name
					//  (found in a backreferrence to the name), so do not do dmang.increment().
					dmang.parseInfoPush(0, "FragmentName from Anonymous Namespace");
					dmang.increment(); // skip the '?'
					nameAnonymous = new MDReusableName(dmang);
					nameAnonymous.parse();
					dmang.parseInfoPop();
					break;
				case 'I': // Believe this is interface namespace
					// 20140522: See note for 'A' anonymous namespace; for 'I' there is no
					// evidence to include the 'I' in the fragment (investigation seems to have
					// it removed).
					dmang.parseInfoPush(0, "InterfaceName from NameFragment");
					dmang.increment(); // skip the '?'
					dmang.increment(); // skip the 'I'
					nameInterface = new MDReusableName(dmang);
					nameInterface.parse();
					dmang.parseInfoPop();
					break;
				case 'C':
					// Windows 10 seems to have "?C" that we don't know how to process.
					//  Best guess at the moment is to read an MDNameFragment next and
					//  keep the fragment's terminating '@'.  'C' used to be part of the
					//  NumberedNamespace below.
					// MDMANG SPECIALIZATION USED.
					if (dmang.processQualCAsSpecialFragment()) {
						dmang.parseInfoPush(0, "NameC");
						dmang.increment(); // skip the '?'
						dmang.increment(); // skip the 'C'
						MDFragmentName fragName = new MDFragmentName(dmang);
						fragName.keepTerminator(); // keeps the terminating '@'
						fragName.parse();
						nameC = fragName;
						dmang.parseInfoPop();
						break;
					}
					// else fall through to MDNumberedNamespace below.
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					// Whether or not intended (I don't think it was), for the cases other
					//  than those that begin with 'A' or 'I', undname processes all of
					//  these as numbered namespaces.  'A' would normally be 0, but 'I'
					//  could be the beginning of a host of numbered namespaces.
				case 'B':
					// See the possible "case 'C'" above, which fixes other issues, but makes
					//  long test go from 73 to 1308 wrong.
//				case 'C':
				case 'D':
				case 'E':
				case 'F':
				case 'G':
				case 'H':
				case 'J':
				case 'K':
				case 'L':
				case 'M':
				case 'N':
				case 'O':
				case 'P':
					dmang.increment(); // skip the '?'
					nameNumbered = new MDNumberedNamespace(dmang);
					nameNumbered.parse();
					break;
				case 'Q':
					// 20160729 Win10 stuff:
					//  "?__abi_GetIids@?QObject@Platform@@?$Array@P$AAVString@Platform@@$00@2@U$AAGJPAKPAPAVGuid@2@@Z_10006bc0";
					// 20170331: Also seen in this symbol:
					//  "?add@?Q?$IObservableMap@HH@Collections@Foundation@Windows@@MapChanged@?$Map@HHU?$less@H@std@@@2Platform@@UE$AAA?AVEventRegistrationToken@34@PE$AAV?$MapChangedEventHandler@HH@234@@Z"
					dmang.parseInfoPush(0, "InterfaceName from QualifiedName");
					dmang.increment(); // skip the '?'
					dmang.increment(); // skip the 'Q'
					MDQualification qualName = new MDQualification(dmang);
					qualName.parse();
					nameQ = qualName;
					dmang.parseInfoPop();
					break;
				default: // special name
					if (!dmang.isLlvmProcessingMode()) {
						throw new MDException("SpecialName not expected in qualification list");
					}
					// Working around LLVM non-compliance with MSFT mangling standard (See
					//  MDNestedName too).  LLVM does not follow the double question mark ("??")
					// convention, but is only using one "?" (which we got above).  Maybe this
					// is not a nested object, but it has attributes of a nested object other than
					// not adhering to what MSFT does.  There is no test we can do such as checking
					// for lower-case letter because an embedded object name could start with
					// one of the capital letters that have meaning in the above switch cases.
					nameNested = new MDNestedName(dmang);
					nameNested.parse();
					break;
			}
		}
		else {
			name = new MDReusableName(dmang);
			name.parse();
		}
	}
}

/******************************************************************************/
/******************************************************************************/
