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

Part of the Fritzing project - http://fritzing.org
Copyright (c) 2007-2019 Fritzing

Fritzing is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Fritzing is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.

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

#include "mysterypart.h"
#include "../utils/graphicsutils.h"
#include "../utils/familypropertycombobox.h"
#include "../utils/schematicrectconstants.h"
#include "../sketch/infographicsview.h"
#include "../utils/textutils.h"
#include "partlabel.h"
#include "pinheader.h"
#include "partfactory.h"
#include "../svg/svgfilesplitter.h"

#include <QDomNodeList>
#include <QDomDocument>
#include <QDomElement>
#include <QLineEdit>

static QStringList Spacings;
static QRegularExpression Digits("(\\d)+");
static QRegularExpression DigitsMil("(\\d)+mil");

static constexpr int MinSipPins = 1;
static constexpr int MaxSipPins = 128;
static constexpr int MinDipPins = 4;
static constexpr int MaxDipPins = 128;

static HoleClassThing TheHoleThing;

// TODO
//	save into parts bin

MysteryPart::MysteryPart( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
	: PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
{
	m_chipLabel = modelPart->localProp("chip label").toString();
	if (m_chipLabel.isEmpty()) {
		m_chipLabel = modelPart->properties().value("chip label", "?");
		modelPart->setLocalProp("chip label", m_chipLabel);
	}

	setUpHoleSizes("mystery", TheHoleThing);

}

MysteryPart::~MysteryPart() {
}

void MysteryPart::setProp(const QString & prop, const QString & value) {
	if (prop.compare("chip label", Qt::CaseInsensitive) == 0) {
		setChipLabel(value, false);
		return;
	}

	PaletteItem::setProp(prop, value);
}

void MysteryPart::setChipLabel(QString chipLabel, bool force) {

	if (!force && m_chipLabel.compare(chipLabel) == 0) return;

	m_chipLabel = chipLabel;

	QString svg;
	switch (this->m_viewID) {
	case ViewLayer::BreadboardView:
		svg = makeSvg(chipLabel, true);
		if (!svg.isEmpty()) {
			resetRenderer(svg);
		}
		break;
	case ViewLayer::SchematicView:
	{
		QTransform  transform = untransform();
		svg = makeSvg(chipLabel, false);
		bool normalized = false;
		svg = retrieveSchematicSvg(svg, normalized);
		resetLayerKin(svg);
		retransform(transform);
	}
	break;
	default:
		break;
	}

	modelPart()->setLocalProp("chip label", chipLabel);

	if (m_partLabel != nullptr) m_partLabel->displayTextsIf();
}

QString MysteryPart::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
{
	QString svg = PaletteItem::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor);
	switch (viewLayerID) {
	case ViewLayer::Breadboard:
	case ViewLayer::Icon:
		return TextUtils::replaceTextElement(svg, "label", m_chipLabel);

	case ViewLayer::Schematic:
	{
		bool normalized = false;
		svg = retrieveSchematicSvg(svg, normalized);
		if (!normalized) {
			SvgFileSplitter splitter;
			bool result = splitter.splitString(svg, "schematic");
			if (result) {
				double factor;
				result = splitter.normalize(dpi, "schematic", blackOnly, factor);
				if (result) {
					svg = splitter.elementString("schematic");
				}
			}
		}
		return TextUtils::removeSVGHeader(svg);
	}
	default:
		break;
	}

	return svg;
}

QString MysteryPart::retrieveSchematicSvg(QString & svg, bool & normalized) {
	bool hasLocal = false;
	QStringList labels = getPinLabels(hasLocal);

	svg = makeSchematicSvg(labels, false);
	normalized = false;

	return TextUtils::replaceTextElement(svg, "label", m_chipLabel);
}


QString MysteryPart::makeSvg(const QString & chipLabel, bool replace) {
	QString path = filename();
	QFile file(filename());
	QString svg;
	if (file.open(QFile::ReadOnly)) {
		svg = file.readAll();
		file.close();
		if (!replace) return svg;

		return TextUtils::replaceTextElement(svg, "label", chipLabel);
	}

	return "";
}

QStringList MysteryPart::collectValues(const QString & family, const QString & prop, QString & value) {
	if (prop.compare("layout", Qt::CaseInsensitive) == 0) {
		// TODO: translate these
		QStringList values;
		values << "Single Row" << "Double Row";
		value = values.at(moduleID().contains("sip", Qt::CaseInsensitive) ? 0 : 1);
		return values;
	}

	if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) {
		QStringList values;
		QString spacing;
		TextUtils::getPinsAndSpacing(moduleID(), spacing);
		if (isDIP()) {
			Q_FOREACH (QString f, spacings()) {
				values.append(f);
			}
		}
		else {
			values.append(spacing);
		}

		value = spacing;
		return values;
	}

	if (prop.compare("pins", Qt::CaseInsensitive) == 0) {
		QStringList values;
		value = modelPart()->properties().value("pins");

		if (moduleID().contains("dip")) {
			for (int i = MinDipPins; i <= MaxDipPins; i += 2) {
				values << QString::number(i);
			}
		}
		else {
			for (int i = MinSipPins; i <= MaxSipPins; i++) {
				values << QString::number(i);
			}
		}

		return values;
	}


	return PaletteItem::collectValues(family, prop, value);
}

bool MysteryPart::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
{
	if (prop.compare("chip label", Qt::CaseInsensitive) == 0) {
		returnProp = tr("label");
		returnValue = m_chipLabel;

		auto * e1 = new QLineEdit(parent);
		e1->setEnabled(swappingEnabled);
		e1->setText(m_chipLabel);
		connect(e1, SIGNAL(editingFinished()), this, SLOT(chipLabelEntry()));
		e1->setObjectName("infoViewLineEdit");


		returnWidget = e1;

		return true;
	}

	if (prop.compare("hole size", Qt::CaseInsensitive) == 0) {
		return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget);
	}

	return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
}

QString MysteryPart::getProperty(const QString & key) {
	if (key.compare("chip label", Qt::CaseInsensitive) == 0) {
		return m_chipLabel;
	}

	return PaletteItem::getProperty(key);
}

QString MysteryPart::chipLabel() {
	return m_chipLabel;
}

void MysteryPart::addedToScene(bool temporary)
{
	if (this->scene() != nullptr) {
		setChipLabel(m_chipLabel, true);
	}

	PaletteItem::addedToScene(temporary);
}

const QString & MysteryPart::title() {
	return m_chipLabel;
}

bool MysteryPart::hasCustomSVG() {
	switch (m_viewID) {
	case ViewLayer::BreadboardView:
	case ViewLayer::SchematicView:
	case ViewLayer::IconView:
	case ViewLayer::PCBView:
		return true;
	default:
		return ItemBase::hasCustomSVG();
	}
}

void MysteryPart::chipLabelEntry() {
	auto * edit = qobject_cast<QLineEdit *>(sender());
	if (edit == nullptr) return;

	if (edit->text().compare(this->chipLabel()) == 0) return;

	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
	if (infoGraphicsView != nullptr) {
		infoGraphicsView->setProp(this, "chip label", tr("chip label"), this->chipLabel(), edit->text(), true);
	}
}

bool MysteryPart::isDIP() {
	QString layout = modelPart()->properties().value("layout", "");
	return (layout.indexOf("double", 0, Qt::CaseInsensitive) >= 0);
}

bool MysteryPart::otherPropsChange(const QMap<QString, QString> & propsMap) {
	QString layout = modelPart()->properties().value("layout", "");
	return (layout.compare(propsMap.value("layout", "")) != 0);
}

const QStringList & MysteryPart::spacings() {
	if (Spacings.count() == 0) {
		Spacings << "100mil" << "200mil" << "300mil" << "400mil" << "500mil" << "600mil" << "700mil" << "800mil" << "900mil";
	}
	return Spacings;
}

ItemBase::PluralType MysteryPart::isPlural() {
	return Plural;
}

QString MysteryPart::genSipFZP(const QString & moduleid)
{
	return genxFZP(moduleid, "mystery_part_sipFzpTemplate", MinSipPins, MaxSipPins, 1);
}

QString MysteryPart::genDipFZP(const QString & moduleid)
{
	return genxFZP(moduleid, "mystery_part_dipFzpTemplate", MinDipPins, MaxDipPins, 2);

}

QString MysteryPart::genxFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int step) {
	QString spacingString;
	TextUtils::getPinsAndSpacing(moduleid, spacingString);
	QString result = PaletteItem::genFZP(moduleid, templateName, minPins, maxPins, step, false);
	result.replace(".percent.", "%");
	result = result.arg(spacingString);
	return hackFzpHoleSize(result, moduleid);
}

QString MysteryPart::hackFzpHoleSize(const QString & fzp, const QString & moduleid) {
	int hsix = moduleid.lastIndexOf(HoleSizePrefix);
	if (hsix >= 0) {
		return PaletteItem::hackFzpHoleSize(fzp, moduleid, hsix);
	}

	return fzp;
}

QString MysteryPart::genModuleID(QMap<QString, QString> & currPropsMap)
{
	QString value = currPropsMap.value("layout");
	bool single = value.contains("single", Qt::CaseInsensitive);
	QString pins = currPropsMap.value("pins");
	QString spacing = currPropsMap.value("pin spacing", "300mil");
	if (single) {
		return QString("mystery_part_sip_%1_100mil").arg(pins);
	}
	else {
		int p = pins.toInt();
		if (p < 4) p = 4;
		if (p % 2 == 1) p--;
		return QString("mystery_part_dip_%1_%2").arg(p).arg(spacing);
	}
}

QString MysteryPart::makeSchematicSvg(const QString & expectedFileName)
{
	bool sip = expectedFileName.contains("sip", Qt::CaseInsensitive);

	QStringList pieces = expectedFileName.split("_");

	int pins = pieces.at(2).toInt();
	QStringList labels;
	for (int i = 0; i < pins; i++) {
		labels << QString::number(i + 1);
	}

	if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) {
		return obsoleteMakeSchematicSvg(labels, sip);
	}

	return makeSchematicSvg(labels, sip);
}

QString MysteryPart::makeSchematicSvg(const QStringList & labels, bool sip)
{
	QDomDocument fakeDoc;

	QList<QDomElement> lefts;
	for (int i = 0; i < labels.count(); i++) {
		QDomElement element = fakeDoc.createElement("contact");
		element.setAttribute("connectorIndex", i);
		element.setAttribute("name", labels.at(i));
		lefts.append(element);
	}
	QList<QDomElement> empty;
	QStringList busNames;

	QString titleText = sip ? "IC" : "?";
	QString svg = SchematicRectConstants::genSchematicDIP(empty, empty, lefts, empty, empty, busNames, titleText, false, false, SchematicRectConstants::simpleGetConnectorName);
	if (sip) return svg;

	// add the mystery part graphic
	QDomDocument doc;
	if (!doc.setContent(svg)) return svg;

	QRectF viewBox;
	double w, h;
	TextUtils::ensureViewBox(doc, GraphicsUtils::StandardFritzingDPI, viewBox, false, w, h, false);

	double newUnit = 1000 * SchematicRectConstants::NewUnit / 25.4;
	double rectStroke = 2 * 1000 * SchematicRectConstants::RectStrokeWidth / 25.4;
	QString circle = QString("<circle cx='%1' cy='%2' r='%3' fill='black' stroke-width='0' stroke='none' />\n");
	circle += QString("<text x='%1' fill='#FFFFFF' y='%4' font-family=\"%5\" text-anchor='middle' font-weight='bold' stroke='none' stroke-width='0' font-size='%6' >?</text>\n");
	circle = circle
	         .arg(viewBox.width() - rectStroke - (newUnit / 2))
	         .arg(rectStroke + (newUnit / 2))
	         .arg(newUnit / 2)

	         .arg(rectStroke + (newUnit / 2) + (newUnit / 3))   // offset so the text appears in the center of the circle
	         .arg(SchematicRectConstants::FontFamily)
	         .arg(newUnit)
	         ;

	QDomDocument temp;
	temp.setContent(QString("<g>" + circle + "</g>"));

	QDomElement root = doc.documentElement();
	QDomElement schematic = TextUtils::findElementWithAttribute(root, "id", "schematic");
	if (schematic.isNull()) return svg;

	QDomElement tempRoot = temp.documentElement();
	schematic.appendChild(tempRoot);

	return doc.toString(1);
}

QString MysteryPart::obsoleteMakeSchematicSvg(const QStringList & labels, bool sip)
{
	int increment = GraphicsUtils::StandardSchematicSeparationMils;   // 7.5mm;
	int border = 30;
	double totalHeight = (labels.count() * increment) + increment + border;
	int textOffset = 50;
	int repeatTextOffset = 50;
	int fontSize = 255;
	int labelFontSize = 130;
	int defaultLabelWidth = 30;
	QString labelText = "?";
	double textMax = defaultLabelWidth;
	QFont font("Droid Sans", labelFontSize * 72 / GraphicsUtils::StandardFritzingDPI, QFont::Normal);
	QFontMetricsF fm(font);
	for (int i = 0; i < labels.count(); i++) {
		double w = fm.horizontalAdvance(labels.at(i));
		if (w > textMax) textMax = w;
	}
	textMax = textMax * GraphicsUtils::StandardFritzingDPI / 72;

	int totalWidth = (5 * increment) + border;
	int innerWidth = 4 * increment;
	if (textMax > defaultLabelWidth) {
		totalWidth += (textMax - defaultLabelWidth);
		innerWidth += (textMax - defaultLabelWidth);
	}

	QString header("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
	               "<svg xmlns:svg='http://www.w3.org/2000/svg' \n"
	               "xmlns='http://www.w3.org/2000/svg' \n"
	               "version='1.2' baseProfile='tiny' \n"
	               "width='%7in' height='%1in' viewBox='0 0 %8 %2'>\n"
	               "<g id='schematic'>\n"
	               "<rect x='315' y='15' fill='none' width='%9' height='%3' stroke='#000000' stroke-linejoin='round' stroke-linecap='round' stroke-width='30' />\n"
	               "<text id='label' x='%11' y='%4' fill='#000000' stroke='none' font-family='Droid Sans' text-anchor='middle' font-size='%5' >%6</text>\n");

	if (!sip) {
		header +=	"<circle fill='#000000' cx='%10' cy='200' r='150' stroke-width='0' />\n"
		            "<text x='%10' fill='#FFFFFF' y='305' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='275' >?</text>\n";
	}
	else {
		labelText = "IC";
		fontSize = 235;
		textOffset = 0;
	}

	QString svg = header
	              .arg(totalHeight / GraphicsUtils::StandardFritzingDPI)
	              .arg(totalHeight)
	              .arg(totalHeight - border)
	              .arg((totalHeight / 2) + textOffset)
	              .arg(fontSize)
	              .arg(labelText)
	              .arg(totalWidth / 1000.0)
	              .arg(totalWidth)
	              .arg(innerWidth)
	              .arg(totalWidth - 200)
	              .arg(increment + textMax + ((totalWidth - increment - textMax) / 2.0))
	              ;


	QString repeat("<line fill='none' stroke='#000000' stroke-linejoin='round' stroke-linecap='round' stroke-width='30' x1='15' y1='%1' x2='300' y2='%1'  />\n"
	               "<rect x='0' y='%2' fill='none' width='300' height='30' id='connector%3pin' stroke-width='0' />\n"
	               "<rect x='0' y='%2' fill='none' width='30' height='30' id='connector%3terminal' stroke-width='0' />\n"
	               "<text id='label%3' x='390' y='%4' font-family='Droid Sans' stroke='none' fill='#000000' text-anchor='start' font-size='%6' >%5</text>\n");

	for (int i = 0; i < labels.count(); i++) {
		svg += repeat
		       .arg(increment + (border / 2) + (i * increment))
		       .arg(increment + (i * increment))
		       .arg(i)
		       .arg(increment + repeatTextOffset + (i * increment))
		       .arg(labels.at(i))
		       .arg(labelFontSize);
	}

	svg += "</g>\n";
	svg += "</svg>\n";
	return svg;
}


QString MysteryPart::makeBreadboardSvg(const QString & expectedFileName)
{
	if (expectedFileName.contains("_sip_")) return makeBreadboardSipSvg(expectedFileName);
	if (expectedFileName.contains("_dip_")) return makeBreadboardDipSvg(expectedFileName);

	return "";
}

QString MysteryPart::makeBreadboardDipSvg(const QString & expectedFileName)
{
	QString spacingString;
	int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString);
	double spacing = TextUtils::convertToInches(spacingString) * 100;

	int increment = 10;

	QString repeatT("<rect id='connector%1terminal' x='[1.87]' y='1' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='0'/>\n"
	                "<rect id='connector%1pin' x='[1.87]' y='0' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='3.5'/>\n");

	QString repeatB("<rect id='connector%1terminal' x='{1.87}' y='[11.0]' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='0'/>\n"
	                "<rect id='connector%1pin' x='{1.87}' y='[7.75]' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='4.25'/>\n");

	QString header("<?xml version='1.0' encoding='utf-8'?>\n"
	               "<svg version='1.2' baseProfile='tiny' xmlns='http://www.w3.org/2000/svg' \n"
	               "width='.percent.1in' height='%1in' viewBox='0 0 {16.0022} [12.0]'>\n"
	               "<g id='breadboard'>\n"
	               ".percent.2\n"
	               "<rect width='{16.0022}' x='0' y='2.5' height='[6.5]' fill='#000000' id='upper' stroke-width='0' />\n"
	               "<rect width='{16.0022}' x='0' y='[6.5]' fill='#404040' height='3.096' id='lower' stroke-width='0' />\n"
	               "<text id='label' x='2.5894' y='{{6.0}}' fill='#e6e6e6' stroke='none' font-family='Droid Sans' text-anchor='start' font-size='7.3' >?</text>\n"
	               "<circle fill='#8C8C8C' cx='11.0022' cy='{{7.5}}' r='3' stroke-width='0' />\n"
	               "<text x='11.0022' y='{{9.2}}' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='5.5' >?</text>\n"
	               ".percent.3\n"
	               "</g>\n"
	               "</svg>\n");


	header = TextUtils::incrementTemplateString(header, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, nullptr);
	header = header.arg(TextUtils::getViewBoxCoord(header, 3) / 100.0);
	if (spacing == 10) {
		header.replace("{{6.0}}", "8.0");
		header.replace("{{7.5}}", "5.5");
		header.replace("{{9.2}}", "7.2");
	}
	else {
		header.replace("{{6.0}}", QString::number(6.0 + ((spacing - increment) / 2)));
		header.replace("{{7.5}}", "7.5");
		header.replace("{{9.2}}", "9.2");
	}
	header.replace(".percent.", "%");
	header.replace("{", "[");
	header.replace("}", "]");

	QString svg = TextUtils::incrementTemplateString(header, 1, increment * ((pins - 4) / 2), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, nullptr);

	repeatB = TextUtils::incrementTemplateString(repeatB, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, nullptr);
	repeatB.replace("{", "[");
	repeatB.replace("}", "]");

	int userData[2];
	userData[0] = pins;
	userData[1] = 1;
	QString repeatTs = TextUtils::incrementTemplateString(repeatT, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData);
	QString repeatBs = TextUtils::incrementTemplateString(repeatB, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, nullptr);

	return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeatTs).arg(repeatBs);
}

QString MysteryPart::makeBreadboardSipSvg(const QString & expectedFileName)
{
	QStringList pieces = expectedFileName.split("_");
	if (pieces.count() != 6) return "";

	int pins = pieces.at(2).toInt();
	int increment = 10;

	QString header("<?xml version='1.0' encoding='utf-8'?>\n"
	               "<svg version='1.2' baseProfile='tiny' id='svg2' xmlns='http://www.w3.org/2000/svg' \n"
	               "width='%1in' height='0.27586in' viewBox='0 0 [6.0022] 27.586'>\n"
	               "<g id='breadboard'>\n"
	               "<rect width='[6.0022]' x='0' y='0' height='24.17675' fill='#000000' id='upper' stroke-width='0' />\n"
	               "<rect width='[6.0022]' x='0' y='22' fill='#404040' height='3.096' id='lower' stroke-width='0' />\n"
	               "<text id='label' x='2.5894' y='13' fill='#e6e6e6' stroke='none' font-family='Droid Sans' text-anchor='start' font-size='7.3' >?</text>\n"
	               "<circle fill='#8C8C8C' cx='[1.0022]' cy='5' r='3' stroke-width='0' />\n"
	               "<text x='[1.0022]' y='6.7' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='5.5' >?</text>\n"
	               "%2\n"
	               "</g>\n"
	               "</svg>\n");

	QString svg = TextUtils::incrementTemplateString(header, 1, increment * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, nullptr);

	QString repeat("<rect id='connector%1terminal' stroke='none' stroke-width='0' x='[1.87]' y='25.586' fill='#8C8C8C' width='2.3' height='2.0'/>\n"
	               "<rect id='connector%1pin' stroke='none' stroke-width='0' x='[1.87]' y='23.336' fill='#8C8C8C' width='2.3' height='4.25'/>\n");

	QString repeats = TextUtils::incrementTemplateString(repeat, pins, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, nullptr);

	return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeats);
}

bool MysteryPart::changePinLabels(bool sip) {

	if (m_viewID != ViewLayer::SchematicView) return true;

	bool hasLocal = false;
	QStringList labels = getPinLabels(hasLocal);
	if (labels.count() == 0) return true;

	QTransform  transform = untransform();

	QString svg = MysteryPart::makeSchematicSvg(labels, sip);

	QString chipLabel = modelPart()->localProp("chip label").toString();
	if (!chipLabel.isEmpty()) {
		svg =TextUtils::replaceTextElement(svg, "label", chipLabel);
	}

	resetLayerKin(svg);

	retransform(transform);

	return true;
}

void MysteryPart::swapEntry(const QString & text) {

	auto * comboBox = qobject_cast<FamilyPropertyComboBox *>(sender());
	if (comboBox == nullptr) return;

	QString layout = m_propsMap.value("layout");

	if (comboBox->prop().contains("layout", Qt::CaseInsensitive)) {
		layout = text;
	}

	if (layout.isEmpty()) {
		if (moduleID().contains("sip", Qt::CaseInsensitive)) {
			layout = "single";
		}
	}

	if (layout.contains("single", Qt::CaseInsensitive)) {
		generateSwap(text, genModuleID, genSipFZP, makeBreadboardSvg, makeSchematicSvg, PinHeader::makePcbSvg);
	}
	else {
		generateSwap(text, genModuleID, genDipFZP, makeBreadboardSvg, makeSchematicSvg, makePcbDipSvg);
	}
	PaletteItem::swapEntry(text);
}

QString MysteryPart::makePcbDipSvg(const QString & expectedFileName)
{
	QString spacingString;
	int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString);
	if (pins == 0) return "";

	QString header("<?xml version='1.0' encoding='UTF-8'?>\n"
	               "<svg baseProfile='tiny' version='1.2' width='%1in' height='%2in' viewBox='0 0 %3 %4' xmlns='http://www.w3.org/2000/svg'>\n"
	               "<desc>Fritzing footprint SVG</desc>\n"
	               "<g id='silkscreen'>\n"
		       "<line stroke='black' stroke-width='10' x1='10' x2='10' y1='10' y2='%5'/>\n"
		       "<line stroke='black' stroke-width='10' x1='10' x2='%6' y1='%5' y2='%5'/>\n"
		       "<line stroke='black' stroke-width='10' x1='%6' x2='%6' y1='%5' y2='10'/>\n"
		       "<line stroke='black' stroke-width='10' x1='10' x2='%7' y1='10' y2='10'/>\n"
		       "<line stroke='black' stroke-width='10' x1='%8' x2='%6' y1='10' y2='10'/>\n"
	               "</g>\n"
	               "<g id='copper1'><g id='copper0'>\n"
	               "<rect id='square' fill='none' height='55' stroke='rgb(255, 191, 0)' stroke-width='20' width='55' x='32.5' y='32.5'/>\n");

	double outerBorder = 10;
	double silkSplitTop = 100;
	double offsetX = 60;
	double offsetY = 60;
	double spacing = TextUtils::convertToInches(spacingString) * GraphicsUtils::StandardFritzingDPI;
	double totalWidth = 120 + spacing;
	double totalHeight = (100 * pins / 2) + (outerBorder * 2);
	double center = totalWidth / 2;

	QString svg = header.arg(totalWidth / GraphicsUtils::StandardFritzingDPI).arg(totalHeight / GraphicsUtils::StandardFritzingDPI).arg(totalWidth).arg(totalHeight)
	              .arg(totalHeight - outerBorder).arg(totalWidth - outerBorder)
	              .arg(center - (silkSplitTop / 2)).arg(center + (silkSplitTop / 2));

	QString circle("<circle cx='%1' cy='%2' fill='none' id='connector%3pin' r='27.5' stroke='rgb(255, 191, 0)' stroke-width='20'/>\n");

	int y = offsetY;
	for (int i = 0; i < pins / 2; i++) {
		svg += circle.arg(offsetX).arg(y).arg(i);
		svg += circle.arg(totalWidth - offsetX).arg(y).arg(pins - 1 - i);
		y += 100;
	}

	svg += "</g></g>\n";
	svg += "</svg>\n";

	int hsix = expectedFileName.indexOf(HoleSizePrefix);
	if (hsix >= 0) {
		return hackSvgHoleSizeAux(svg, expectedFileName);
	}

	return svg;
}
